提交 cf22c641 authored 作者: Thomas Mueller's avatar Thomas Mueller

new experimental page store

上级 3d361d59
...@@ -240,7 +240,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -240,7 +240,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
// with the old table // with the old table
// still need a new id because using 0 would mean: the new table tries // still need a new id because using 0 would mean: the new table tries
// to use the rows of the table 0 (the meta table) // to use the rows of the table 0 (the meta table)
int id = -1; int id = db.allocateObjectId(true, true);
TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent, false, Index.EMPTY_HEAD); TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent, false, Index.EMPTY_HEAD);
newTable.setComment(table.getComment()); newTable.setComment(table.getComment());
StringBuffer buff = new StringBuffer(newTable.getCreateSQL()); StringBuffer buff = new StringBuffer(newTable.getCreateSQL());
......
...@@ -201,7 +201,7 @@ class PageBtreeNode extends PageBtree { ...@@ -201,7 +201,7 @@ class PageBtreeNode extends PageBtree {
return false; return false;
} }
// this child is now empty // this child is now empty
index.getPageStore().freePage(page.getPageId()); index.getPageStore().freePage(page.getPageId(), true, page.data);
if (entryCount < 1) { if (entryCount < 1) {
// no more children - this page is empty as well // no more children - this page is empty as well
return true; return true;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
package org.h2.index; package org.h2.index;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Session; import org.h2.engine.Session;
...@@ -92,8 +93,6 @@ class PageDataLeaf extends PageData { ...@@ -92,8 +93,6 @@ class PageDataLeaf extends PageData {
int addRow(Row row) throws SQLException { int addRow(Row row) throws SQLException {
int rowLength = row.getByteCount(data); int rowLength = row.getByteCount(data);
int pageSize = index.getPageStore().getPageSize(); int pageSize = index.getPageStore().getPageSize();
// TODO currently the order is important
// TODO and can only add at the end
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1]; int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
if (entryCount > 0 && last - rowLength < start + KEY_OFFSET_PAIR_LENGTH) { if (entryCount > 0 && last - rowLength < start + KEY_OFFSET_PAIR_LENGTH) {
int todoSplitAtLastInsertionPoint; int todoSplitAtLastInsertionPoint;
...@@ -112,11 +111,15 @@ class PageDataLeaf extends PageData { ...@@ -112,11 +111,15 @@ class PageDataLeaf extends PageData {
System.arraycopy(keys, 0, newKeys, 0, x); System.arraycopy(keys, 0, newKeys, 0, x);
System.arraycopy(rows, 0, newRows, 0, x); System.arraycopy(rows, 0, newRows, 0, x);
if (x < entryCount) { if (x < entryCount) {
System.arraycopy(offsets, x, newOffsets, x + 1, entryCount - x); for (int j = x; j < entryCount; j++) {
newOffsets[j + 1] = offsets[j] - rowLength;
}
System.arraycopy(keys, x, newKeys, x + 1, entryCount - x); System.arraycopy(keys, x, newKeys, x + 1, entryCount - x);
System.arraycopy(rows, x, newRows, x + 1, entryCount - x); System.arraycopy(rows, x, newRows, x + 1, entryCount - x);
} }
} }
last = x == 0 ? pageSize : offsets[x - 1];
offset = last - rowLength;
entryCount++; entryCount++;
start += KEY_OFFSET_PAIR_LENGTH; start += KEY_OFFSET_PAIR_LENGTH;
newOffsets[x] = offset; newOffsets[x] = offset;
...@@ -175,7 +178,11 @@ class PageDataLeaf extends PageData { ...@@ -175,7 +178,11 @@ class PageDataLeaf extends PageData {
System.arraycopy(offsets, 0, newOffsets, 0, i); System.arraycopy(offsets, 0, newOffsets, 0, i);
System.arraycopy(keys, 0, newKeys, 0, i); System.arraycopy(keys, 0, newKeys, 0, i);
System.arraycopy(rows, 0, newRows, 0, i); System.arraycopy(rows, 0, newRows, 0, i);
System.arraycopy(offsets, i + 1, newOffsets, i, entryCount - i); int startNext = i < entryCount - 1 ? offsets[i + 1] : index.getPageStore().getPageSize();
int rowLength = offsets[i] - startNext;
for (int j = i; j < entryCount; j++) {
newOffsets[j] = offsets[j + 1] + rowLength;
}
System.arraycopy(keys, i + 1, newKeys, i, entryCount - i); System.arraycopy(keys, i + 1, newKeys, i, entryCount - i);
System.arraycopy(rows, i + 1, newRows, i, entryCount - i); System.arraycopy(rows, i + 1, newRows, i, entryCount - i);
start -= KEY_OFFSET_PAIR_LENGTH; start -= KEY_OFFSET_PAIR_LENGTH;
...@@ -335,6 +342,9 @@ class PageDataLeaf extends PageData { ...@@ -335,6 +342,9 @@ class PageDataLeaf extends PageData {
if (firstOverflowPageId != 0) { if (firstOverflowPageId != 0) {
data.writeInt(firstOverflowPageId); data.writeInt(firstOverflowPageId);
} }
if (getPos() == 1) {
System.out.println("pause");
}
for (int i = 0; i < entryCount; i++) { for (int i = 0; i < entryCount; i++) {
data.writeInt(keys[i]); data.writeInt(keys[i]);
data.writeShortInt(offsets[i]); data.writeShortInt(offsets[i]);
......
...@@ -196,7 +196,7 @@ class PageDataNode extends PageData { ...@@ -196,7 +196,7 @@ class PageDataNode extends PageData {
return false; return false;
} }
// this child is now empty // this child is now empty
index.getPageStore().freePage(page.getPageId()); index.getPageStore().freePage(page.getPageId(), true, page.data);
if (entryCount < 1) { if (entryCount < 1) {
// no more children - this page is empty as well // no more children - this page is empty as well
return true; return true;
...@@ -217,6 +217,9 @@ class PageDataNode extends PageData { ...@@ -217,6 +217,9 @@ class PageDataNode extends PageData {
int count = 0; int count = 0;
for (int i = 0; i < childPageIds.length; i++) { for (int i = 0; i < childPageIds.length; i++) {
PageData page = index.getPage(childPageIds[i]); PageData page = index.getPage(childPageIds[i]);
if (getPos() == page.getPos()) {
throw Message.throwInternalError("Page it its own child: " + getPageId());
}
count += page.getRowCount(); count += page.getRowCount();
} }
rowCount = count; rowCount = count;
......
...@@ -40,7 +40,7 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -40,7 +40,7 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
public PageScanIndex(TableData table, int id, IndexColumn[] columns, IndexType indexType, int headPos) throws SQLException { public PageScanIndex(TableData table, int id, IndexColumn[] columns, IndexType indexType, int headPos) throws SQLException {
initBaseIndex(table, id, table.getName() + "_TABLE_SCAN", columns, indexType); initBaseIndex(table, id, table.getName() + "_TABLE_SCAN", columns, indexType);
int test; int test;
// trace.setLevel(TraceSystem.DEBUG); trace.setLevel(TraceSystem.DEBUG);
if (database.isMultiVersion()) { if (database.isMultiVersion()) {
int todoMvcc; int todoMvcc;
} }
...@@ -85,9 +85,15 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -85,9 +85,15 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
public void add(Session session, Row row) throws SQLException { public void add(Session session, Row row) throws SQLException {
if (row.getPos() == 0) { if (row.getPos() == 0) {
row.setPos(++lastKey); row.setPos(++lastKey);
} else {
lastKey = Math.max(lastKey, row.getPos() + 1);
} }
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("add " + row.getPos()); int test;
if (table.getId() == -1) {
System.out.println("pause");
}
trace.debug("add table:" + table.getId() + " " + row);
} }
if (tableData.getContainsLargeObject()) { if (tableData.getContainsLargeObject()) {
for (int i = 0; i < row.getColumnCount(); i++) { for (int i = 0; i < row.getColumnCount(); i++) {
...@@ -191,6 +197,9 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -191,6 +197,9 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
public void remove(Session session, Row row) throws SQLException { public void remove(Session session, Row row) throws SQLException {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("remove " + row.getPos()); trace.debug("remove " + row.getPos());
if (table.getId() == -1) {
System.out.println("pause");
}
} }
if (tableData.getContainsLargeObject()) { if (tableData.getContainsLargeObject()) {
for (int i = 0; i < row.getColumnCount(); i++) { for (int i = 0; i < row.getColumnCount(); i++) {
......
...@@ -112,7 +112,7 @@ public class PageInputStream extends InputStream { ...@@ -112,7 +112,7 @@ public class PageInputStream extends InputStream {
remaining = store.getPageSize() - page.length(); remaining = store.getPageSize() - page.length();
} }
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("pageIn.readPage " + parentPage + " next:" + nextPage); // trace.debug("pageIn.readPage " + parentPage + " next:" + nextPage);
} }
} catch (SQLException e) { } catch (SQLException e) {
throw Message.convertToIOException(e); throw Message.convertToIOException(e);
......
...@@ -234,7 +234,7 @@ e.printStackTrace(); ...@@ -234,7 +234,7 @@ e.printStackTrace();
reservedPages[i] = store.allocatePage(); reservedPages[i] = store.allocatePage();
} }
for (int i = 0; i < pageCount; i++) { for (int i = 0; i < pageCount; i++) {
store.freePage(reservedPages[i]); store.freePage(reservedPages[i], false, null);
} }
} }
......
...@@ -168,7 +168,7 @@ public class PageStore implements CacheWriter { ...@@ -168,7 +168,7 @@ public class PageStore implements CacheWriter {
this.database = database; this.database = database;
trace = database.getTrace(Trace.PAGE_STORE); trace = database.getTrace(Trace.PAGE_STORE);
int test; int test;
// trace.setLevel(TraceSystem.DEBUG); trace.setLevel(TraceSystem.DEBUG);
this.cacheSize = cacheSizeDefault; this.cacheSize = cacheSizeDefault;
String cacheType = database.getCacheType(); String cacheType = database.getCacheType();
if (Cache2Q.TYPE_NAME.equals(cacheType)) { if (Cache2Q.TYPE_NAME.equals(cacheType)) {
...@@ -407,6 +407,10 @@ public class PageStore implements CacheWriter { ...@@ -407,6 +407,10 @@ public class PageStore implements CacheWriter {
synchronized (database) { synchronized (database) {
Record record = (Record) obj; Record record = (Record) obj;
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
int test;
if (record.getPos() == 1) {
System.out.println("pause");
}
trace.debug("writeBack " + record); trace.debug("writeBack " + record);
} }
int todoRemoveParameter; int todoRemoveParameter;
...@@ -426,6 +430,10 @@ public class PageStore implements CacheWriter { ...@@ -426,6 +430,10 @@ public class PageStore implements CacheWriter {
synchronized (database) { synchronized (database) {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
if (!record.isChanged()) { if (!record.isChanged()) {
int test;
if(record.getPos() == 1) {
System.out.println("pause");
}
trace.debug("updateRecord " + record.toString()); trace.debug("updateRecord " + record.toString());
} }
} }
...@@ -481,7 +489,7 @@ public class PageStore implements CacheWriter { ...@@ -481,7 +489,7 @@ public class PageStore implements CacheWriter {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("allocated " + id + " atEnd:" + atEnd); trace.debug("allocated " + id + " atEnd:" + atEnd);
} }
if (id > pageCount) { if (id >= pageCount) {
increaseFileSize(INCREMENT_PAGES); increaseFileSize(INCREMENT_PAGES);
} }
return id; return id;
...@@ -498,8 +506,10 @@ public class PageStore implements CacheWriter { ...@@ -498,8 +506,10 @@ public class PageStore implements CacheWriter {
* Add a page to the free list. * Add a page to the free list.
* *
* @param pageId the page id * @param pageId the page id
* @param logUndo if an undo entry need to be logged
* @param old the old data (if known)
*/ */
public void freePage(int pageId) throws SQLException { public void freePage(int pageId, boolean logUndo, DataPage old) throws SQLException {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("freePage " + pageId); trace.debug("freePage " + pageId);
} }
...@@ -507,7 +517,13 @@ public class PageStore implements CacheWriter { ...@@ -507,7 +517,13 @@ public class PageStore implements CacheWriter {
getFreeList().free(pageId); getFreeList().free(pageId);
if (recoveryRunning) { if (recoveryRunning) {
writePage(pageId, createDataPage()); writePage(pageId, createDataPage());
} else if (logUndo) {
if (old == null) {
old = readPage(pageId);
}
getLog().addUndo(pageId, old);
} }
} }
/** /**
...@@ -626,9 +642,8 @@ public class PageStore implements CacheWriter { ...@@ -626,9 +642,8 @@ public class PageStore implements CacheWriter {
* @param undo true if the undo step should be run * @param undo true if the undo step should be run
*/ */
private void recover(boolean undo) throws SQLException { private void recover(boolean undo) throws SQLException {
if (undo) { trace.debug("log recover #" + undo);
trace.debug("log recover"); if (!undo) {
} else {
openMetaIndex(); openMetaIndex();
readMetaData(); readMetaData();
} }
...@@ -849,6 +864,9 @@ public class PageStore implements CacheWriter { ...@@ -849,6 +864,9 @@ public class PageStore implements CacheWriter {
meta = table.getScanIndex(database.getSystemSession()); meta = table.getScanIndex(database.getSystemSession());
} else { } else {
PageScanIndex p = (PageScanIndex) metaObjects.get(ObjectUtils.getInteger(parent)); PageScanIndex p = (PageScanIndex) metaObjects.get(ObjectUtils.getInteger(parent));
if (p == null) {
throw Message.throwInternalError("parent not found:" + parent);
}
TableData table = (TableData) p.getTable(); TableData table = (TableData) p.getTable();
Column[] tableCols = table.getColumns(); Column[] tableCols = table.getColumns();
Column[] cols = new Column[columns.length]; Column[] cols = new Column[columns.length];
...@@ -861,6 +879,11 @@ public class PageStore implements CacheWriter { ...@@ -861,6 +879,11 @@ public class PageStore implements CacheWriter {
metaObjects.put(ObjectUtils.getInteger(id), meta); metaObjects.put(ObjectUtils.getInteger(id), meta);
} }
/**
* Add the meta data of an index.
*
* @param index the index to add
*/
public void addMeta(Index index) throws SQLException { public void addMeta(Index index) throws SQLException {
int type = index instanceof PageScanIndex ? META_TYPE_SCAN_INDEX : META_TYPE_BTREE_INDEX; int type = index instanceof PageScanIndex ? META_TYPE_SCAN_INDEX : META_TYPE_BTREE_INDEX;
Column[] columns = index.getColumns(); Column[] columns = index.getColumns();
...@@ -883,6 +906,11 @@ public class PageStore implements CacheWriter { ...@@ -883,6 +906,11 @@ public class PageStore implements CacheWriter {
metaIndex.add(database.getSystemSession(), row); metaIndex.add(database.getSystemSession(), row);
} }
/**
* Remove the meta data of an index.
*
* @param index the index to remove
*/
public void removeMeta(Index index) throws SQLException { public void removeMeta(Index index) throws SQLException {
Session session = database.getSystemSession(); Session session = database.getSystemSession();
Row row = metaIndex.getRow(session, index.getId() + 1); Row row = metaIndex.getRow(session, index.getId() + 1);
......
...@@ -15,37 +15,180 @@ import java.io.InputStream; ...@@ -15,37 +15,180 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Random; import java.util.Random;
import org.h2.constant.SysProperties;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.Page; import org.h2.index.Page;
import org.h2.index.PageScanIndex;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.PageInputStream; import org.h2.store.PageInputStream;
import org.h2.store.PageOutputStream; import org.h2.store.PageOutputStream;
import org.h2.store.PageStore; import org.h2.store.PageStore;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.IntArray; import org.h2.util.IntArray;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
import org.h2.value.ValueInt;
/** /**
* Test the page store. * Test the page store.
*/ */
public class TestPageStore extends TestBase { public class TestPageStore extends TestBase {
private Database db;
private Schema schema;
private TableData table;
private Index index;
/** /**
* Run just this test. * Run just this test.
* *
* @param a ignored * @param a ignored
*/ */
public static void main(String[] a) throws Exception { public static void main(String[] a) throws Exception {
System.setProperty("h2.pageStore", "true");
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
public void test() throws Exception { public void test() throws Exception {
testAllocateFree(); testScanIndex();
testStreamFuzz(); testBtreeIndex();
testStreamPerformance(false, 1000); // testAllocateFree();
// testStreamFuzz();
// testStreamPerformance(false, 1000);
// testPerformance(true, 1000000); // testPerformance(true, 1000000);
// testPerformance(false, 1000000); // testPerformance(false, 1000000);
} }
private void testBtreeIndex() throws SQLException {
if (!SysProperties.PAGE_STORE) {
return;
}
deleteDb("pageStore");
String fileName = getTestDir("/pageStore");
new File(fileName).delete();
File f = new File(fileName + ".dat");
f.delete();
db = getDatabase();
PageStore store = new PageStore(db, fileName, "rw", 8192);
store.setPageSize(1024);
store.open();
openBtreeIndex();
Row row;
for (int i = 10; i < 100; i += 10) {
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(i));
row.setPos(i);
index.add(db.getSystemSession(), row);
}
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(60));
row.setPos(60);
index.remove(db.getSystemSession(), row);
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(60));
row.setPos(60);
index.add(db.getSystemSession(), row);
store.checkpoint();
store.close();
store = new PageStore(db, fileName, "rw", 8192);
store.open();
openBtreeIndex();
Cursor cursor = index.find(db.getSystemSession(), null, null);
for (int i = 10; i < 100; i += 10) {
assertTrue(cursor.next());
Row r = cursor.get();
assertEquals(i, r.getValue(0).getInt());
}
assertFalse(cursor.next());
store.close();
db.shutdownImmediately();
}
private void testScanIndex() throws SQLException {
if (!SysProperties.PAGE_STORE) {
return;
}
deleteDb("pageStore");
String fileName = getTestDir("/pageStore");
new File(fileName).delete();
File f = new File(fileName + ".dat");
f.delete();
db = getDatabase();
PageStore store = new PageStore(db, fileName, "rw", 8192);
store.setPageSize(1024);
store.open();
openScanIndex();
Row row;
for (int i = 10; i < 100; i += 10) {
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(i));
row.setPos(i);
index.add(db.getSystemSession(), row);
}
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(60));
row.setPos(60);
index.remove(db.getSystemSession(), row);
row = table.getTemplateRow();
row.setValue(0, ValueInt.get(60));
row.setPos(60);
index.add(db.getSystemSession(), row);
store.checkpoint();
store.close();
store = new PageStore(db, fileName, "rw", 8192);
store.open();
openScanIndex();
Cursor cursor = index.find(db.getSystemSession(), null, null);
for (int i = 10; i < 100; i += 10) {
assertTrue(cursor.next());
Row r = cursor.get();
assertEquals(i, r.getValue(0).getInt());
}
assertFalse(cursor.next());
store.close();
db.shutdownImmediately();
}
private Database getDatabase() throws SQLException {
String name = getTestDir("/pageStore");
ConnectionInfo ci = new ConnectionInfo(name);
return new Database(name, ci, null);
}
private void openScanIndex() throws SQLException {
ObjectArray cols = new ObjectArray();
cols.add(new Column("ID", Value.INT));
schema = new Schema(db, 0, "", null, true);
table = new TableData(schema, "PAGE_INDEX",
1, cols, true, false, 100);
index = (PageScanIndex) table.getScanIndex(
db.getSystemSession());
}
private void openBtreeIndex() throws SQLException {
ObjectArray cols = new ObjectArray();
cols.add(new Column("ID", Value.INT));
schema = new Schema(db, 0, "", null, true);
int id = db.allocateObjectId(true, true);
table = new TableData(schema, "BTREE_INDEX",
id, cols, true, false, 100);
id = db.allocateObjectId(true, true);
table.addIndex(db.getSystemSession(), "BTREE", id,
IndexColumn.wrap(table.getColumns()),
IndexType.createNonUnique(true),
Index.EMPTY_HEAD, "");
index = (PageScanIndex) table.getScanIndex(
db.getSystemSession());
}
private void testAllocateFree() throws SQLException { private void testAllocateFree() throws SQLException {
String fileName = getTestDir("/pageStore"); String fileName = getTestDir("/pageStore");
new File(fileName).delete(); new File(fileName).delete();
...@@ -63,7 +206,7 @@ public class TestPageStore extends TestBase { ...@@ -63,7 +206,7 @@ public class TestPageStore extends TestBase {
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int id = list.get(i); int id = list.get(i);
store.freePage(id); store.freePage(id, false, null);
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
int id = store.allocatePage(); int id = store.allocatePage();
...@@ -76,12 +219,6 @@ public class TestPageStore extends TestBase { ...@@ -76,12 +219,6 @@ public class TestPageStore extends TestBase {
f.delete(); f.delete();
} }
private Database getDatabase() throws SQLException {
String name = "mem:pageStore";
ConnectionInfo ci = new ConnectionInfo(name);
return new Database(name, ci, null);
}
private void testStreamPerformance(boolean file, int count) throws Exception { private void testStreamPerformance(boolean file, int count) throws Exception {
String fileName = getTestDir("/pageStore"); String fileName = getTestDir("/pageStore");
new File(fileName).delete(); new File(fileName).delete();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论