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

Page store: refactor to support compacting

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