提交 1b19a060 authored 作者: Thomas Mueller's avatar Thomas Mueller

Page store: refactor to support compacting

上级 c40a4e9a
......@@ -9,12 +9,17 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.Record;
import org.h2.store.Page;
/**
* A page that contains index data.
*/
abstract class PageBtree extends Record {
public abstract class PageBtree extends Page {
/**
* This is a root page.
*/
static final int ROOT = 0;
/**
* Indicator that the row count is not known.
......@@ -66,9 +71,8 @@ abstract class PageBtree extends Record {
*/
protected boolean written;
PageBtree(PageBtreeIndex index, int pageId, int parentPageId, Data data) {
PageBtree(PageBtreeIndex index, int pageId, Data data) {
this.index = index;
this.parentPageId = parentPageId;
this.data = data;
setPos(pageId);
}
......@@ -129,11 +133,6 @@ abstract class PageBtree extends Record {
return l;
}
/**
* Read the data.
*/
abstract void read() throws SQLException;
/**
* Add a row if possible. If it is possible this method returns -1, otherwise
* the split point. It is always possible to add one row.
......
......@@ -16,7 +16,6 @@ import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.DataPage;
import org.h2.store.PageStore;
import org.h2.store.Record;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
......@@ -46,6 +45,7 @@ public class PageBtreeIndex extends BaseIndex {
throw Message.throwInternalError("" + indexName);
}
this.store = database.getPageStore();
store.addIndex(this);
if (headPos == Index.EMPTY_HEAD) {
// new index
needRebuild = true;
......@@ -54,7 +54,8 @@ public class PageBtreeIndex extends BaseIndex {
// it should not for new tables, otherwise redo of other operations
// must ensure this page is not used for other things
store.addMeta(this, session, headPos);
PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createData());
PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, store.createData());
root.parentPageId = PageBtree.ROOT;
store.updateRecord(root, true, root.data);
} else {
this.headPos = headPos;
......@@ -101,7 +102,8 @@ public class PageBtreeIndex extends BaseIndex {
page1.setPageId(id);
page1.setParentPageId(headPos);
page2.setParentPageId(headPos);
PageBtreeNode newRoot = new PageBtreeNode(this, rootPageId, Page.ROOT, store.createData());
PageBtreeNode newRoot = new PageBtreeNode(this, rootPageId, store.createData());
newRoot.parentPageId = PageBtree.ROOT;
newRoot.init(page1, pivot, page2);
store.updateRecord(page1, true, page1.data);
store.updateRecord(page2, true, page2.data);
......@@ -134,39 +136,13 @@ public class PageBtreeIndex extends BaseIndex {
* @return the page
*/
PageBtree getPage(int id) throws SQLException {
Record rec = store.getRecord(id);
if (rec != null) {
if (SysProperties.CHECK) {
if (!(rec instanceof PageBtree)) {
throw Message.throwInternalError("Wrong page: " + rec + " " + this);
}
PageBtree result = (PageBtree) rec;
if (result.index.headPos != this.headPos) {
throw Message.throwInternalError("Wrong index: " + result.index + " " + this);
}
}
return (PageBtree) rec;
}
Data data = store.readPage(id);
data.reset();
int parentPageId = data.readInt();
int type = data.readByte() & 255;
PageBtree result;
switch (type & ~Page.FLAG_LAST) {
case Page.TYPE_BTREE_LEAF:
result = new PageBtreeLeaf(this, id, parentPageId, data);
break;
case Page.TYPE_BTREE_NODE:
result = new PageBtreeNode(this, id, parentPageId, data);
break;
case Page.TYPE_EMPTY:
PageBtreeLeaf empty = new PageBtreeLeaf(this, id, parentPageId, data);
PageBtree p = (PageBtree) store.getPage(id);
if (p == null) {
Data data = store.createData();
PageBtreeLeaf empty = new PageBtreeLeaf(this, id, data);
return empty;
default:
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "page=" + id + " type=" + type);
}
result.read();
return result;
return p;
}
public boolean canGetFirstOrLast() {
......@@ -276,7 +252,8 @@ public class PageBtreeIndex extends BaseIndex {
private void removeAllRows() throws SQLException {
PageBtree root = getPage(headPos);
root.freeChildren();
root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createData());
root = new PageBtreeLeaf(this, headPos, store.createData());
root.parentPageId = PageBtree.ROOT;
store.removeRecord(headPos);
store.updateRecord(root, true, null);
rowCount = 0;
......
......@@ -13,37 +13,53 @@ import org.h2.message.Message;
import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.DataPage;
import org.h2.store.Page;
/**
* 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>5-8: index id
* </li><li>9-10: entry count
* </li><li>11-: list of key / offset pairs (4 bytes key, 2 bytes offset)
* </li><li>data
* </li></ul>
*/
class PageBtreeLeaf extends PageBtree {
public class PageBtreeLeaf extends PageBtree {
private static final int OFFSET_LENGTH = 2;
private static final int OFFSET_START = 11;
PageBtreeLeaf(PageBtreeIndex index, int pageId, int parentPageId, Data data) {
super(index, pageId, parentPageId, data);
PageBtreeLeaf(PageBtreeIndex index, int pageId, Data data) {
super(index, pageId, data);
start = OFFSET_START;
}
void read() throws SQLException {
data.setPos(4);
/**
* Read a b-tree leaf page.
*
* @param index the index
* @param data the data
* @param pageId the page id
* @return the page
*/
public static Page read(PageBtreeIndex index, Data data, int pageId) throws SQLException {
PageBtreeLeaf p = new PageBtreeLeaf(index, pageId, data);
p.read();
return p;
}
private void read() throws SQLException {
data.reset();
this.parentPageId = data.readInt();
int type = data.readByte();
onlyPosition = (type & Page.FLAG_LAST) == 0;
int tableId = data.readInt();
if (tableId != index.getId()) {
int indexId = data.readInt();
if (indexId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
"page:" + getPos() + " expected table:" + index.getId() +
"got:" + tableId);
"page:" + getPos() + " expected index:" + index.getId() +
"got:" + indexId);
}
entryCount = data.readShortInt();
offsets = new int[entryCount];
......@@ -137,7 +153,8 @@ class PageBtreeLeaf extends PageBtree {
PageBtree split(int splitPoint) throws SQLException {
int newPageId = index.getPageStore().allocatePage();
PageBtreeLeaf p2 = new PageBtreeLeaf(index, newPageId, parentPageId, index.getPageStore().createData());
PageBtreeLeaf p2 = new PageBtreeLeaf(index, newPageId, index.getPageStore().createData());
p2.parentPageId = parentPageId;
for (int i = splitPoint; i < entryCount;) {
p2.addRowTry(getRow(splitPoint));
removeRow(splitPoint);
......@@ -216,7 +233,7 @@ class PageBtreeLeaf extends PageBtree {
void find(PageBtreeCursor cursor, SearchRow first, boolean bigger) throws SQLException {
int i = find(first, bigger, false, false);
if (i > entryCount) {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
return;
}
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
......@@ -240,7 +257,7 @@ class PageBtreeLeaf extends PageBtree {
* @param cursor the cursor
*/
void nextPage(PageBtreeCursor cursor) throws SQLException {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
cursor.setCurrent(null, 0);
return;
}
......@@ -254,7 +271,7 @@ class PageBtreeLeaf extends PageBtree {
* @param cursor the cursor
*/
void previousPage(PageBtreeCursor cursor) throws SQLException {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
cursor.setCurrent(null, 0);
return;
}
......
......@@ -7,11 +7,13 @@
package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.DataPage;
import org.h2.store.Page;
import org.h2.store.PageStore;
import org.h2.util.MemoryUtils;
......@@ -21,18 +23,19 @@ import org.h2.util.MemoryUtils;
* <ul>
* <li>0-3: parent page id</li>
* <li>4-4: page type</li>
* <li>5-6: entry count</li>
* <li>7-10: row count of all children (-1 if not known)</li>
* <li>11-14: rightmost child page id</li>
* <li>15- entries: 4 bytes leaf page id, 4 bytes offset to data</li>
* <li>5-8: index id</li>
* <li>9-10: entry count</li>
* <li>11-14: row count of all children (-1 if not known)</li>
* <li>15-18: rightmost child page id</li>
* <li>19- entries: 4 bytes leaf page id, 4 bytes offset to data</li>
* </ul>
* The row is the largest row of the respective child, meaning
* row[0] is the largest row of child[0].
*/
class PageBtreeNode extends PageBtree {
public class PageBtreeNode extends PageBtree {
private static final int CHILD_OFFSET_PAIR_LENGTH = 8;
private static final int CHILD_OFFSET_PAIR_START = 15;
private static final int CHILD_OFFSET_PAIR_START = 19;
/**
* The page ids of the children.
......@@ -43,15 +46,36 @@ class PageBtreeNode extends PageBtree {
private int rowCount = UNKNOWN_ROWCOUNT;
PageBtreeNode(PageBtreeIndex index, int pageId, int parentPageId, Data data) {
super(index, pageId, parentPageId, data);
PageBtreeNode(PageBtreeIndex index, int pageId, Data data) {
super(index, pageId, data);
start = CHILD_OFFSET_PAIR_START;
}
void read() {
data.setPos(4);
/**
* Read a b-tree node page.
*
* @param index the index
* @param data the data
* @param pageId the page id
* @return the page
*/
public static Page read(PageBtreeIndex index, Data data, int pageId) throws SQLException {
PageBtreeNode p = new PageBtreeNode(index, pageId, data);
p.read();
return p;
}
private void read() throws SQLException {
data.reset();
this.parentPageId = data.readInt();
int type = data.readByte();
onlyPosition = (type & Page.FLAG_LAST) == 0;
int indexId = data.readInt();
if (indexId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
"page:" + getPos() + " expected index:" + index.getId() +
"got:" + indexId);
}
entryCount = data.readShortInt();
rowCount = data.readInt();
if (!PageStore.STORE_BTREE_ROWCOUNT) {
......@@ -176,7 +200,8 @@ class PageBtreeNode extends PageBtree {
PageBtree split(int splitPoint) throws SQLException {
int newPageId = index.getPageStore().allocatePage();
PageBtreeNode p2 = new PageBtreeNode(index, newPageId, parentPageId, index.getPageStore().createData());
PageBtreeNode p2 = new PageBtreeNode(index, newPageId, index.getPageStore().createData());
p2.parentPageId = parentPageId;
if (onlyPosition) {
// TODO optimize: maybe not required
p2.onlyPosition = true;
......@@ -226,7 +251,7 @@ class PageBtreeNode extends PageBtree {
void find(PageBtreeCursor cursor, SearchRow first, boolean bigger) throws SQLException {
int i = find(first, bigger, false, false);
if (i > entryCount) {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
return;
}
PageBtreeNode next = (PageBtreeNode) index.getPage(parentPageId);
......@@ -338,6 +363,7 @@ class PageBtreeNode extends PageBtree {
data.reset();
data.writeInt(parentPageId);
data.writeByte((byte) (Page.TYPE_BTREE_NODE | (onlyPosition ? 0 : Page.FLAG_LAST)));
data.writeInt(index.getId());
data.writeShortInt(entryCount);
data.writeInt(rowCount);
data.writeInt(childPageIds[entryCount]);
......@@ -404,7 +430,7 @@ class PageBtreeNode extends PageBtree {
}
}
if (i > entryCount) {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
cursor.setCurrent(null, 0);
return;
}
......@@ -433,7 +459,7 @@ class PageBtreeNode extends PageBtree {
}
}
if (i < 0) {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageBtree.ROOT) {
cursor.setCurrent(null, 0);
return;
}
......
......@@ -10,12 +10,17 @@ import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.store.Data;
import org.h2.store.Record;
import org.h2.store.Page;
/**
* A page that contains data rows.
*/
abstract class PageData extends Record {
abstract class PageData extends Page {
/**
* This is a root page.
*/
static final int ROOT = 0;
/**
* Indicator that the row count is not known.
......@@ -52,9 +57,8 @@ abstract class PageData extends Record {
*/
protected boolean written;
PageData(PageScanIndex index, int pageId, int parentPageId, Data data) {
PageData(PageScanIndex index, int pageId, Data data) {
this.index = index;
this.parentPageId = parentPageId;
this.data = data;
setPos(pageId);
}
......@@ -95,11 +99,6 @@ abstract class PageData extends Record {
return l;
}
/**
* Read the data.
*/
abstract void read() throws SQLException;
/**
* Add a row if possible. If it is possible this method returns -1, otherwise
* the split point. It is always possible to add one row.
......
......@@ -15,6 +15,7 @@ import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.store.Data;
import org.h2.store.DataPage;
import org.h2.store.Page;
import org.h2.store.PageStore;
/**
......@@ -29,7 +30,7 @@ import org.h2.store.PageStore;
* </li><li>data
* </li></ul>
*/
class PageDataLeaf extends PageData {
public class PageDataLeaf extends PageData {
private static final int KEY_OFFSET_PAIR_LENGTH = 6;
private static final int KEY_OFFSET_PAIR_START = 11;
......@@ -64,13 +65,28 @@ class PageDataLeaf extends PageData {
*/
private int overflowRowSize;
PageDataLeaf(PageScanIndex index, int pageId, int parentPageId, Data data) {
super(index, pageId, parentPageId, data);
PageDataLeaf(PageScanIndex index, int pageId, Data data) {
super(index, pageId, data);
start = KEY_OFFSET_PAIR_START;
}
void read() throws SQLException {
data.setPos(4);
/**
* Read a data leaf page.
*
* @param index the index
* @param data the data
* @param pageId the page id
* @return the page
*/
public static Page read(PageScanIndex index, Data data, int pageId) throws SQLException {
PageDataLeaf p = new PageDataLeaf(index, pageId, data);
p.read();
return p;
}
private void read() throws SQLException {
data.reset();
this.parentPageId = data.readInt();
int type = data.readByte();
int tableId = data.readInt();
if (tableId != index.getId()) {
......@@ -170,7 +186,7 @@ class PageDataLeaf extends PageData {
size = pageSize - PageDataOverflow.START_MORE;
next = index.getPageStore().allocatePage();
}
PageDataOverflow overflow = new PageDataOverflow(this, page, type, previous, next, data, dataOffset, size);
PageDataOverflow overflow = new PageDataOverflow(index, page, type, previous, next, data, dataOffset, size);
index.getPageStore().updateRecord(overflow, true, null);
dataOffset += size;
remaining -= size;
......@@ -232,9 +248,8 @@ class PageDataLeaf extends PageData {
int pageSize = store.getPageSize();
data.setPos(pageSize);
int next = firstOverflowPageId;
int offset = pageSize;
do {
PageDataOverflow page = index.getPageOverflow(next, this, offset);
PageDataOverflow page = index.getPageOverflow(next);
next = page.readInto(data);
} while (next != 0);
overflowRowSize = data.length();
......@@ -257,7 +272,8 @@ class PageDataLeaf extends PageData {
PageData split(int splitPoint) throws SQLException {
int newPageId = index.getPageStore().allocatePage();
PageDataLeaf p2 = new PageDataLeaf(index, newPageId, parentPageId, index.getPageStore().createData());
PageDataLeaf p2 = new PageDataLeaf(index, newPageId, index.getPageStore().createData());
p2.parentPageId = parentPageId;
for (int i = splitPoint; i < entryCount;) {
p2.addRowTry(getRowAt(splitPoint));
removeRow(splitPoint);
......@@ -274,7 +290,7 @@ class PageDataLeaf extends PageData {
}
PageDataLeaf getNextPage() throws SQLException {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageData.ROOT) {
return null;
}
PageDataNode next = (PageDataNode) index.getPage(parentPageId, -1);
......@@ -289,7 +305,7 @@ class PageDataLeaf extends PageData {
if (firstOverflowPageId == 0) {
return;
}
PageDataOverflow overflow = index.getPageOverflow(firstOverflowPageId, this, 0);
PageDataOverflow overflow = index.getPageOverflow(firstOverflowPageId);
overflow.setParent(getPos());
index.getPageStore().updateRecord(overflow, true, null);
}
......@@ -312,7 +328,7 @@ class PageDataLeaf extends PageData {
PageStore store = index.getPageStore();
int next = firstOverflowPageId;
do {
PageDataOverflow page = index.getPageOverflow(next, this, 0);
PageDataOverflow page = index.getPageOverflow(next);
store.freePage(next, false, null);
next = page.getNextOverflow();
} while (next != 0);
......
......@@ -7,12 +7,13 @@
package org.h2.index;
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.Data;
import org.h2.store.DataPage;
import org.h2.store.Page;
import org.h2.util.MemoryUtils;
/**
......@@ -20,17 +21,18 @@ import org.h2.util.MemoryUtils;
* Format:
* <ul><li>0-3: parent page id
* </li><li>4-4: page type
* </li><li>5-6: entry count
* </li><li>7-10: row count of all children (-1 if not known)
* </li><li>11-14: rightmost child page id
* </li><li>15- entries: 4 bytes leaf page id, 4 bytes key
* </li><li>5-8: index id
* </li><li>9-10: entry count
* </li><li>11-14: row count of all children (-1 if not known)
* </li><li>15-18: rightmost child page id
* </li><li>19- entries: 4 bytes leaf page id, 4 bytes key
* </li></ul>
* The key is the largest key of the respective child, meaning
* key[0] is the largest key of child[0].
*/
class PageDataNode extends PageData {
public class PageDataNode extends PageData {
private static final int ENTRY_START = 15;
private static final int ENTRY_START = 19;
private static final int ENTRY_LENGTH = 8;
......@@ -43,12 +45,34 @@ class PageDataNode extends PageData {
private int rowCount = UNKNOWN_ROWCOUNT;
PageDataNode(PageScanIndex index, int pageId, int parentPageId, Data data) {
super(index, pageId, parentPageId, data);
PageDataNode(PageScanIndex index, int pageId, Data data) {
super(index, pageId, data);
}
/**
* Read a data node page.
*
* @param index the index
* @param data the data
* @param pageId the page id
* @return the page
*/
public static Page read(PageScanIndex index, Data data, int pageId) throws SQLException {
PageDataNode p = new PageDataNode(index, pageId, data);
p.read();
return p;
}
void read() {
data.setPos(5);
private void read() throws SQLException {
data.reset();
this.parentPageId = data.readInt();
data.readByte();
int indexId = data.readInt();
if (indexId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
"page:" + getPos() + " expected index:" + index.getId() +
"got:" + indexId);
}
entryCount = data.readShortInt();
rowCount = rowCountStored = data.readInt();
childPageIds = new int[entryCount + 1];
......@@ -122,7 +146,8 @@ class PageDataNode extends PageData {
PageData split(int splitPoint) throws SQLException {
int newPageId = index.getPageStore().allocatePage();
PageDataNode p2 = new PageDataNode(index, newPageId, parentPageId, index.getPageStore().createData());
PageDataNode p2 = new PageDataNode(index, newPageId, index.getPageStore().createData());
p2.parentPageId = parentPageId;
int firstChild = childPageIds[splitPoint];
for (int i = splitPoint; i < entryCount;) {
p2.addChild(p2.entryCount, childPageIds[splitPoint + 1], keys[splitPoint]);
......@@ -171,7 +196,7 @@ class PageDataNode extends PageData {
PageDataLeaf getNextPage(int key) throws SQLException {
int i = find(key) + 1;
if (i > entryCount) {
if (parentPageId == Page.ROOT) {
if (parentPageId == PageData.ROOT) {
return null;
}
PageDataNode next = (PageDataNode) index.getPage(parentPageId, -1);
......@@ -271,6 +296,7 @@ class PageDataNode extends PageData {
data.reset();
data.writeInt(parentPageId);
data.writeByte((byte) Page.TYPE_DATA_NODE);
data.writeInt(index.getId());
data.writeShortInt(entryCount);
data.writeInt(rowCountStored);
data.writeInt(childPageIds[entryCount]);
......
......@@ -11,29 +11,30 @@ import org.h2.constant.ErrorCode;
import org.h2.message.Message;
import org.h2.store.Data;
import org.h2.store.DataPage;
import org.h2.store.Record;
import org.h2.store.Page;
/**
* Overflow data for a leaf page.
* Format:
* <ul><li>0-3: parent page id (0 for root)
* </li><li>4-4: page type
* </li><li>if there is more data: 5-8: next overflow page id
* </li><li>otherwise: 5-6: remaining size
* </li><li>5-8: index id
* </li><li>if there is more data: 9-12: next overflow page id
* </li><li>otherwise: 9-10: remaining size
* </li><li>data
* </li></ul>
*/
public class PageDataOverflow extends Record {
public class PageDataOverflow extends Page {
/**
* The start of the data in the last overflow page.
*/
static final int START_LAST = 7;
static final int START_LAST = 11;
/**
* The start of the data in a overflow page that is not the last one.
*/
static final int START_MORE = 9;
static final int START_MORE = 13;
/**
* The index.
......@@ -62,8 +63,8 @@ public class PageDataOverflow extends Record {
private Data data;
PageDataOverflow(PageDataLeaf leaf, int pageId, int type, int previous, int next, Data allData, int offset, int size) {
this.index = leaf.index;
PageDataOverflow(PageScanIndex index, int pageId, int type, int previous, int next, Data allData, int offset, int size) {
this.index = index;
setPos(pageId);
this.type = type;
this.parentPage = previous;
......@@ -72,6 +73,7 @@ public class PageDataOverflow extends Record {
data = index.getPageStore().createData();
data.writeInt(parentPage);
data.writeByte((byte) type);
data.writeInt(index.getId());
if (type == Page.TYPE_DATA_OVERFLOW) {
data.writeInt(nextPage);
} else {
......@@ -88,18 +90,39 @@ public class PageDataOverflow extends Record {
* @param data the data page
* @param offset the offset
*/
PageDataOverflow(PageDataLeaf leaf, int pageId, Data data, int offset) {
this.index = leaf.index;
PageDataOverflow(PageScanIndex index, int pageId, Data data) {
this.index = index;
setPos(pageId);
this.data = data;
}
/**
* Read an overflow page.
*
* @param index the index
* @param data the data
* @param pageId the page id
* @return the page
*/
public static Page read(PageScanIndex index, Data data, int pageId) throws SQLException {
PageDataOverflow p = new PageDataOverflow(index, pageId, data);
p.read();
return p;
}
/**
* Read the page.
*/
void read() throws SQLException {
private void read() throws SQLException {
data.reset();
parentPage = data.readInt();
type = data.readByte();
int indexId = data.readInt();
if (indexId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
"page:" + getPos() + " expected index:" + index.getId() +
" got:" + indexId + " type:" + type);
}
if (type == (Page.TYPE_DATA_OVERFLOW | Page.FLAG_LAST)) {
size = data.readShortInt();
nextPage = 0;
......
......@@ -12,8 +12,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.log.UndoLogRecord;
......@@ -22,7 +20,6 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.store.Data;
import org.h2.store.PageStore;
import org.h2.store.Record;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
......@@ -56,6 +53,7 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
}
tableData = table;
this.store = database.getPageStore();
store.addIndex(this);
if (!database.isPersistent()) {
this.headPos = 0;
throw Message.throwInternalError(table.getName());
......@@ -67,7 +65,8 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
// it should not for new tables, otherwise redo of other operations
// must ensure this page is not used for other things
store.addMeta(this, session, headPos);
PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createData());
PageDataLeaf root = new PageDataLeaf(this, headPos, store.createData());
root.parentPageId = PageData.ROOT;
store.updateRecord(root, true, root.data);
} else {
this.headPos = headPos;
......@@ -129,7 +128,8 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
page1.setPageId(id);
page1.setParentPageId(headPos);
page2.setParentPageId(headPos);
PageDataNode newRoot = new PageDataNode(this, rootPageId, Page.ROOT, store.createData());
PageDataNode newRoot = new PageDataNode(this, rootPageId, store.createData());
newRoot.parentPageId = PageData.ROOT;
newRoot.init(page1, pivot, page2);
store.updateRecord(page1, true, page1.data);
store.updateRecord(page2, true, page2.data);
......@@ -156,20 +156,10 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
* Read an overflow page page.
*
* @param id the page id
* @param leaf the leaf page
* @param offset the offset in bytes
* @return the page
*/
PageDataOverflow getPageOverflow(int id, PageDataLeaf leaf, int offset) throws SQLException {
Record rec = store.getRecord(id);
if (rec != null) {
return (PageDataOverflow) rec;
}
Data data = store.readPage(id);
data.reset();
PageDataOverflow result = new PageDataOverflow(leaf, id, data, offset);
result.read();
return result;
PageDataOverflow getPageOverflow(int id) throws SQLException {
return (PageDataOverflow) store.getPage(id);
}
/**
......@@ -180,41 +170,22 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
* @return the page
*/
PageData getPage(int id, int parent) throws SQLException {
Record rec = store.getRecord(id);
if (rec != null) {
if (SysProperties.CHECK) {
PageData result = (PageData) rec;
if (result.index.headPos != headPos) {
throw Message.throwInternalError("Wrong index: " + result.index.getName() + ":" + result.index.headPos + " " + getName() + ":" + headPos);
}
}
return (PageData) rec;
}
Data data = store.readPage(id);
data.reset();
int parentPageId = data.readInt();
int type = data.readByte() & 255;
PageData result;
switch (type & ~Page.FLAG_LAST) {
case Page.TYPE_DATA_LEAF:
result = new PageDataLeaf(this, id, parentPageId, data);
break;
case Page.TYPE_DATA_NODE:
result = new PageDataNode(this, id, parentPageId, data);
break;
case Page.TYPE_EMPTY:
PageDataLeaf empty = new PageDataLeaf(this, id, parentPageId, data);
PageData p = (PageData) store.getPage(id);
if (p == null) {
Data data = store.createData();
PageDataLeaf empty = new PageDataLeaf(this, id, data);
empty.parentPageId = parent;
return empty;
default:
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "page=" + id + " type=" + type);
}
result.read();
if (p.index.headPos != headPos) {
throw Message.throwInternalError("Wrong index: " + p.index.getName() + ":" + p.index.headPos + " " + getName() + ":" + headPos);
}
if (parent != -1) {
if (result.getParentPageId() != parent) {
throw Message.throwInternalError(result + " parent " + result.getParentPageId() + " expected " + parent);
if (p.getParentPageId() != parent) {
throw Message.throwInternalError(p + " parent " + p.getParentPageId() + " expected " + parent);
}
}
return result;
return p;
}
public boolean canGetFirstOrLast() {
......@@ -308,7 +279,8 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
private void removeAllRows() throws SQLException {
PageData root = getPage(headPos, 0);
root.freeChildren();
root = new PageDataLeaf(this, headPos, Page.ROOT, store.createData());
root = new PageDataLeaf(this, headPos, store.createData());
root.parentPageId = PageData.ROOT;
store.removeRecord(headPos);
store.updateRecord(root, true, null);
rowCount = 0;
......
......@@ -4,7 +4,8 @@
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
package org.h2.store;
/**
* A page. Format:
......@@ -13,7 +14,7 @@ package org.h2.index;
* </li><li>page-type specific data
* </li></ul>
*/
public class Page {
public abstract class Page extends Record {
/**
* This is the last page of a chain.
......@@ -65,9 +66,4 @@ public class Page {
*/
public static final int TYPE_STREAM_DATA = 8;
/**
* This is a root page.
*/
static final int ROOT = 0;
}
......@@ -7,7 +7,6 @@ package org.h2.store;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.index.Page;
import org.h2.message.Message;
import org.h2.util.BitField;
......@@ -20,7 +19,7 @@ import org.h2.util.BitField;
* <li>5-remainder: data</li>
* </ul>
*/
public class PageFreeList extends Record {
public class PageFreeList extends Page {
private static final int DATA_START = 5;
......@@ -30,13 +29,39 @@ public class PageFreeList extends Record {
private boolean full;
private Data data;
PageFreeList(PageStore store, int pageId) {
private PageFreeList(PageStore store, int pageId) {
setPos(pageId);
this.store = store;
pageCount = (store.getPageSize() - DATA_START) * 8;
used.set(0);
}
/**
* Read a free-list page.
*
* @param store the page store
* @param data the data
* @param pageId the page id
* @return the page
*/
static PageFreeList read(PageStore store, Data data, int pageId) throws SQLException {
PageFreeList p = new PageFreeList(store, pageId);
p.data = data;
p.read();
return p;
}
/**
* Create a new free-list page.
*
* @param store the page store
* @param pageId the page id
* @return the page
*/
static PageFreeList create(PageStore store, int pageId) {
return new PageFreeList(store, pageId);
}
/**
* Allocate a page from the free list.
*
......@@ -114,15 +139,11 @@ public class PageFreeList extends Record {
/**
* Read the page from the disk.
*/
void read() throws SQLException {
data = store.createData();
store.readPage(getPos(), data);
private void read() throws SQLException {
data.reset();
int p = data.readInt();
int t = data.readByte();
if (t == Page.TYPE_EMPTY) {
return;
}
if (t != Page.TYPE_FREE_LIST || p != 0) {
if (p != 0) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "pos:" + getPos() + " type:" + t + " parent:" + p
+ " expected type:" + Page.TYPE_FREE_LIST);
}
......
......@@ -76,7 +76,7 @@ public class PageInputStream extends InputStream {
}
}
private void fillBuffer() throws SQLException {
private void fillBuffer() throws SQLException, EOFException {
if (remaining > 0 || endOfFile) {
return;
}
......@@ -87,8 +87,11 @@ public class PageInputStream extends InputStream {
int next;
while (true) {
if (trunk == null) {
trunk = new PageStreamTrunk(store, trunkNext);
trunk.read();
trunk = (PageStreamTrunk) store.getPage(trunkNext);
if (trunk == null) {
throw new EOFException();
}
trunk.resetIndex();
trunkNext = trunk.getNextTrunk();
}
if (trunk != null) {
......@@ -104,8 +107,11 @@ public class PageInputStream extends InputStream {
trace.debug("pageIn.readPage " + next);
}
dataPage = -1;
data = new PageStreamData(store, next, 0);
data.read();
data = (PageStreamData) store.getPage(next);
if (data == null) {
throw new EOFException();
}
data.initRead();
remaining = data.getLength();
}
......@@ -120,8 +126,11 @@ public class PageInputStream extends InputStream {
while (trunkPage != 0) {
pages.set(trunkPage);
store.allocatePage(trunkPage);
PageStreamTrunk t = new PageStreamTrunk(store, trunkPage);
t.read();
PageStreamTrunk t = (PageStreamTrunk) store.getPage(trunkPage);
if (t == null) {
break;
}
t.resetIndex();
while (true) {
int n = t.getNextPageData();
if (n == -1) {
......
......@@ -189,10 +189,8 @@ public class PageLog {
if (store.getRecord(firstTrunkPage) != null) {
throw Message.throwInternalError("" + store.getRecord(firstTrunkPage));
}
PageStreamTrunk t = new PageStreamTrunk(store, this.firstTrunkPage);
try {
t.read();
} catch (SQLException e) {
PageStreamTrunk t = (PageStreamTrunk) store.getPage(this.firstTrunkPage);
if (t == null) {
store.freePage(firstTrunkPage, false, null);
// EOF
break;
......@@ -621,9 +619,8 @@ public class PageLog {
private int removeUntil(int firstTrunkPage, int firstDataPageToKeep) throws SQLException {
trace.debug("log.removeUntil " + firstDataPageToKeep);
while (true) {
// TODO keep trunk page in the cache
PageStreamTrunk t = new PageStreamTrunk(store, firstTrunkPage);
t.read();
PageStreamTrunk t = (PageStreamTrunk) store.getPage(firstTrunkPage);
t.resetIndex();
if (t.contains(firstDataPageToKeep)) {
return t.getPos();
}
......@@ -725,8 +722,7 @@ public class PageLog {
* @param commit whether the transaction should be committed
*/
void setInDoubtTransactionState(int sessionId, int pageId, boolean commit) throws SQLException {
PageStreamData d = new PageStreamData(store, pageId, 0);
d.read();
PageStreamData d = (PageStreamData) store.getPage(pageId);
d.initWrite();
ByteArrayOutputStream buff = new ByteArrayOutputStream();
DataOutputStream o = new DataOutputStream(buff);
......
......@@ -98,13 +98,13 @@ public class PageOutputStream extends OutputStream {
pageIds[i] = reservedPages.get(i);
}
trunkNext = reservedPages.get(len);
trunk = new PageStreamTrunk(store, parent, trunkPageId, trunkNext, pageIds);
trunk = PageStreamTrunk.create(store, parent, trunkPageId, trunkNext, pageIds);
pages++;
trunk.write(null);
reservedPages.removeRange(0, len + 1);
nextData = trunk.getNextPageData();
}
data = new PageStreamData(store, nextData, trunk.getPos());
data = PageStreamData.create(store, nextData, trunk.getPos());
pages++;
data.initWrite();
}
......
......@@ -18,8 +18,12 @@ import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.Page;
import org.h2.index.PageBtreeIndex;
import org.h2.index.PageBtreeLeaf;
import org.h2.index.PageBtreeNode;
import org.h2.index.PageDataLeaf;
import org.h2.index.PageDataNode;
import org.h2.index.PageDataOverflow;
import org.h2.index.PageScanIndex;
import org.h2.log.InDoubtTransaction;
import org.h2.log.LogSystem;
......@@ -192,7 +196,7 @@ public class PageStore implements CacheWriter {
private Schema metaSchema;
private TableData metaTable;
private PageScanIndex metaIndex;
private HashMap<Integer, Index> metaObjects;
private HashMap<Integer, Index> metaObjects = New.hashMap();
/**
* The map of reserved pages, to ensure index head pages
......@@ -360,32 +364,87 @@ public class PageStore implements CacheWriter {
if (free == -1 || free < 10) {
return;
}
Record rec = getRecord(full);
if (rec == null) {
Page p = getPage(full);
}
} else {
Data page = Data.create(database, pageSize);
readPage(full, page);
int parent = page.readInt();
int type = page.readByte();
boolean last = (type & Page.FLAG_LAST) != 0;
type = type & ~Page.FLAG_LAST;
System.out.println("last page is " + type + " parent: " + parent);
switch (type) {
case Page.TYPE_EMPTY:
break;
case Page.TYPE_FREE_LIST:
break;
case Page.TYPE_DATA_LEAF:
break;
case Page.TYPE_DATA_NODE:
case Page.TYPE_DATA_OVERFLOW:
case Page.TYPE_BTREE_LEAF:
case Page.TYPE_BTREE_NODE:
case Page.TYPE_STREAM_TRUNK:
case Page.TYPE_STREAM_DATA:
/**
* Read a page from the store.
*
* @param pageId the page id
* @return the page
*/
public Page getPage(int pageId) throws SQLException {
Record rec = getRecord(pageId);
if (rec != null) {
return (Page) rec;
}
Data data = Data.create(database, pageSize);
readPage(pageId, data);
data.readInt();
int type = data.readByte();
Page p;
switch (type & ~Page.FLAG_LAST) {
case Page.TYPE_EMPTY:
return null;
case Page.TYPE_FREE_LIST:
p = PageFreeList.read(this, data, pageId);
break;
case Page.TYPE_DATA_LEAF: {
int indexId = data.readInt();
PageScanIndex index = (PageScanIndex) metaObjects.get(indexId);
if (index == null) {
Message.throwInternalError("index not found " + indexId);
}
p = PageDataLeaf.read(index, data, pageId);
break;
}
case Page.TYPE_DATA_NODE: {
int indexId = data.readInt();
PageScanIndex index = (PageScanIndex) metaObjects.get(indexId);
if (index == null) {
Message.throwInternalError("index not found " + indexId);
}
p = PageDataNode.read(index, data, pageId);
break;
}
case Page.TYPE_DATA_OVERFLOW: {
int indexId = data.readInt();
PageScanIndex index = (PageScanIndex) metaObjects.get(indexId);
if (index == null) {
Message.throwInternalError("index not found " + indexId);
}
p = PageDataOverflow.read(index, data, pageId);
break;
}
case Page.TYPE_BTREE_LEAF: {
int indexId = data.readInt();
PageBtreeIndex index = (PageBtreeIndex) metaObjects.get(indexId);
if (index == null) {
Message.throwInternalError("index not found " + indexId);
}
p = PageBtreeLeaf.read(index, data, pageId);
break;
}
case Page.TYPE_BTREE_NODE: {
int indexId = data.readInt();
PageBtreeIndex index = (PageBtreeIndex) metaObjects.get(indexId);
if (index == null) {
Message.throwInternalError("index not found " + indexId);
}
p = PageBtreeNode.read(index, data, pageId);
break;
}
case Page.TYPE_STREAM_TRUNK:
p = PageStreamTrunk.read(this, data, pageId);
break;
case Page.TYPE_STREAM_DATA:
p = PageStreamData.read(this, data, pageId);
break;
default:
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "page=" + pageId + " type=" + type);
}
cache.put(p);
return p;
}
/**
......@@ -608,12 +667,12 @@ public class PageStore implements CacheWriter {
while (p >= pageCount) {
increaseFileSize(INCREMENT_PAGES);
}
PageFreeList list = (PageFreeList) getRecord(p);
PageFreeList list = null;
if (p < pageCount) {
list = (PageFreeList) getPage(p);
}
if (list == null) {
list = new PageFreeList(this, p);
if (p < pageCount) {
list.read();
}
list = PageFreeList.create(this, p);
cache.put(list);
}
return list;
......@@ -987,7 +1046,7 @@ public class PageStore implements CacheWriter {
META_TABLE_ID, cols, false, true, true, false, headPos, systemSession);
metaIndex = (PageScanIndex) metaTable.getScanIndex(
systemSession);
metaObjects = New.hashMap();
metaObjects.clear();
metaObjects.put(-1, metaIndex);
}
......@@ -1076,6 +1135,15 @@ public class PageStore implements CacheWriter {
metaObjects.put(id, meta);
}
/**
* Add an index to the in-memory index map.
*
* @param index the index
*/
public void addIndex(Index index) {
metaObjects.put(index.getId(), index);
}
/**
* Add the meta data of an index.
*
......@@ -1124,6 +1192,7 @@ public class PageStore implements CacheWriter {
public void removeMeta(Index index, Session session) throws SQLException {
if (!recoveryRunning) {
removeMetaIndex(index, session);
metaObjects.remove(index.getId());
}
}
......
......@@ -7,9 +7,6 @@
package org.h2.store;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.index.Page;
import org.h2.message.Message;
/**
* A data page of a stream. The format is:
......@@ -20,7 +17,7 @@ import org.h2.message.Message;
* <li>9-remainder: data</li>
* </ul>
*/
public class PageStreamData extends Record {
public class PageStreamData extends Page {
private static final int LENGTH_START = 5;
private static final int DATA_START = 9;
......@@ -31,25 +28,47 @@ public class PageStreamData extends Record {
private int remaining;
private int length;
PageStreamData(PageStore store, int pageId, int trunk) {
private PageStreamData(PageStore store, int pageId, int trunk) {
setPos(pageId);
this.store = store;
this.trunk = trunk;
}
/**
* Read a stream data page.
*
* @param store the page store
* @param data the data
* @param pageId the page id
* @return the page
*/
static PageStreamData read(PageStore store, Data data, int pageId) {
PageStreamData p = new PageStreamData(store, pageId, 0);
p.data = data;
p.read();
return p;
}
/**
* Create a new stream trunk page.
*
* @param store the page store
* @param pageId the page id
* @param trunk the trunk page
* @return the page
*/
static PageStreamData create(PageStore store, int pageId, int trunk) {
return new PageStreamData(store, pageId, trunk);
}
/**
* Read the page from the disk.
*/
void read() throws SQLException {
data = store.createData();
store.readPage(getPos(), data);
private void read() {
data.reset();
trunk = data.readInt();
data.setPos(4);
int t = data.readByte();
if (t != Page.TYPE_STREAM_DATA) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "pos:" + getPos() + " type:" + t +
" expected type:" + Page.TYPE_STREAM_DATA);
}
data.readByte();
length = data.readInt();
}
......@@ -133,4 +152,12 @@ public class PageStreamData extends Record {
return store.getPageSize() >> 2;
}
/**
* Reset the index.
*/
void initRead() {
data.setPos(DATA_START);
remaining = length;
}
}
\ No newline at end of file
......@@ -7,10 +7,6 @@
package org.h2.store;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.index.Page;
import org.h2.message.Message;
import org.h2.util.MemoryUtils;
/**
* A trunk page of a stream. It contains the page numbers of the stream, and
......@@ -23,7 +19,7 @@ import org.h2.util.MemoryUtils;
* <li>13-remainder: page ids</li>
* </ul>
*/
public class PageStreamTrunk extends Record {
public class PageStreamTrunk extends Page {
private static final int DATA_START = 13;
......@@ -35,7 +31,7 @@ public class PageStreamTrunk extends Record {
private Data data;
private int index;
PageStreamTrunk(PageStore store, int parent, int pageId, int next, int[] pageIds) {
private PageStreamTrunk(PageStore store, int parent, int pageId, int next, int[] pageIds) {
setPos(pageId);
this.parent = parent;
this.store = store;
......@@ -44,28 +40,47 @@ public class PageStreamTrunk extends Record {
this.pageIds = pageIds;
}
PageStreamTrunk(PageStore store, int pageId) {
private PageStreamTrunk(PageStore store, Data data, int pageId) {
setPos(pageId);
this.data = data;
this.store = store;
}
/**
* Read a stream trunk page.
*
* @param store the page store
* @param data the data
* @param pageId the page id
* @return the page
*/
static PageStreamTrunk read(PageStore store, Data data, int pageId) {
PageStreamTrunk p = new PageStreamTrunk(store, data, pageId);
p.read();
return p;
}
/**
* Create a new stream trunk page.
*
* @param store the page store
* @param parent the parent page
* @param pageId the page id
* @param next the next trunk page
* @param pageIds the stream data page ids
* @return the page
*/
static PageStreamTrunk create(PageStore store, int parent, int pageId, int next, int[] pageIds) {
return new PageStreamTrunk(store, parent, pageId, next, pageIds);
}
/**
* Read the page from the disk.
*/
void read() throws SQLException {
data = store.createData();
store.readPage(getPos(), data);
private void read() {
data.reset();
parent = data.readInt();
int t = data.readByte();
if (t == Page.TYPE_EMPTY) {
// end of file
pageIds = MemoryUtils.EMPTY_INTS;
return;
}
if (t != Page.TYPE_STREAM_TRUNK) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "pos:" + getPos() + " type:" + t + " parent:" + parent
+ " expected type:" + Page.TYPE_STREAM_TRUNK);
}
data.readByte();
nextTrunk = data.readInt();
pageCount = data.readInt();
pageIds = new int[pageCount];
......@@ -74,6 +89,13 @@ public class PageStreamTrunk extends Record {
}
}
/**
* Reset the read/write index.
*/
void resetIndex() {
index = 0;
}
void setNextDataPage(int page) {
pageIds[index++] = page;
}
......
......@@ -27,7 +27,6 @@ import org.h2.command.Parser;
import org.h2.engine.Constants;
import org.h2.engine.DbObject;
import org.h2.engine.MetaRecord;
import org.h2.index.Page;
import org.h2.log.LogFile;
import org.h2.message.Message;
import org.h2.message.Trace;
......@@ -41,6 +40,7 @@ import org.h2.store.DiskFile;
import org.h2.store.FileLister;
import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.store.Page;
import org.h2.store.PageFreeList;
import org.h2.store.PageLog;
import org.h2.store.PageStore;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论