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

new experimental page store

上级 a0021b99
...@@ -63,6 +63,7 @@ import org.h2.util.FileUtils; ...@@ -63,6 +63,7 @@ import org.h2.util.FileUtils;
import org.h2.util.IntHashMap; import org.h2.util.IntHashMap;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
...@@ -100,6 +101,7 @@ public class Database implements DataHandler { ...@@ -100,6 +101,7 @@ public class Database implements DataHandler {
private final HashMap aggregates = new HashMap(); private final HashMap aggregates = new HashMap();
private final HashMap comments = new HashMap(); private final HashMap comments = new HashMap();
private IntHashMap tableMap = new IntHashMap(); private IntHashMap tableMap = new IntHashMap();
private final HashMap databaseObjects = new HashMap();
private final Set userSessions = Collections.synchronizedSet(new HashSet()); private final Set userSessions = Collections.synchronizedSet(new HashSet());
private Session exclusiveSession; private Session exclusiveSession;
...@@ -519,9 +521,7 @@ public class Database implements DataHandler { ...@@ -519,9 +521,7 @@ public class Database implements DataHandler {
isReconnectNeeded(); isReconnectNeeded();
if (SysProperties.PAGE_STORE) { if (SysProperties.PAGE_STORE) {
PageStore store = getPageStore(); PageStore store = getPageStore();
if (!store.isNew()) { store.recover();
store.recover(true);
}
} }
if (FileUtils.exists(dataFileName)) { if (FileUtils.exists(dataFileName)) {
lobFilesInDirectories &= !ValueLob.existsLobFile(getDatabasePath()); lobFilesInDirectories &= !ValueLob.existsLobFile(getDatabasePath());
...@@ -579,7 +579,7 @@ public class Database implements DataHandler { ...@@ -579,7 +579,7 @@ public class Database implements DataHandler {
cols.add(new Column("SQL", Value.STRING)); cols.add(new Column("SQL", Value.STRING));
int headPos = 0; int headPos = 0;
if (pageStore != null) { if (pageStore != null) {
headPos = pageStore.getSystemRootPageId(); headPos = pageStore.getMetaTableHeadPos();
} }
meta = mainSchema.createTable("SYS", 0, cols, persistent, false, headPos); meta = mainSchema.createTable("SYS", 0, cols, persistent, false, headPos);
tableMap.put(0, meta); tableMap.put(0, meta);
...@@ -606,14 +606,6 @@ public class Database implements DataHandler { ...@@ -606,14 +606,6 @@ public class Database implements DataHandler {
MetaRecord rec = (MetaRecord) records.get(i); MetaRecord rec = (MetaRecord) records.get(i);
rec.execute(this, systemSession, eventListener); rec.execute(this, systemSession, eventListener);
} }
if (pageStore != null) {
if (!pageStore.isNew()) {
getPageStore().recover(false);
if (!readOnly) {
pageStore.checkpoint();
}
}
}
// try to recompile the views that are invalid // try to recompile the views that are invalid
recompileInvalidViews(systemSession); recompileInvalidViews(systemSession);
starting = false; starting = false;
...@@ -630,6 +622,10 @@ public class Database implements DataHandler { ...@@ -630,6 +622,10 @@ public class Database implements DataHandler {
traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName); traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName);
} }
public Schema getMainSchema() {
return mainSchema;
}
private void startServer(String key) throws SQLException { private void startServer(String key) throws SQLException {
server = Server.createTcpServer(new String[]{ server = Server.createTcpServer(new String[]{
"-tcpPort", "0", "-tcpPort", "0",
...@@ -764,18 +760,21 @@ public class Database implements DataHandler { ...@@ -764,18 +760,21 @@ public class Database implements DataHandler {
} }
private synchronized void addMeta(Session session, DbObject obj) throws SQLException { private synchronized void addMeta(Session session, DbObject obj) throws SQLException {
if (obj.getTemporary()) { int id = obj.getId();
return; if (id > 0 && !starting && !obj.getTemporary()) {
Row r = meta.getTemplateRow();
MetaRecord rec = new MetaRecord(obj);
rec.setRecord(r);
objectIds.set(id);
meta.lock(session, true, true);
meta.addRow(session, r);
if (isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the moment
session.log(meta, UndoLogRecord.INSERT, r);
}
} }
Row r = meta.getTemplateRow(); if (SysProperties.PAGE_STORE && id > 0) {
MetaRecord rec = new MetaRecord(obj); databaseObjects.put(ObjectUtils.getInteger(id), obj);
rec.setRecord(r);
objectIds.set(obj.getId());
meta.lock(session, true, true);
meta.addRow(session, r);
if (isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the moment
session.log(meta, UndoLogRecord.INSERT, r);
} }
} }
...@@ -786,23 +785,28 @@ public class Database implements DataHandler { ...@@ -786,23 +785,28 @@ public class Database implements DataHandler {
* @param id the id of the object to remove * @param id the id of the object to remove
*/ */
public synchronized void removeMeta(Session session, int id) throws SQLException { public synchronized void removeMeta(Session session, int id) throws SQLException {
SearchRow r = meta.getTemplateSimpleRow(false); if (id > 0 && !starting) {
r.setValue(0, ValueInt.get(id)); SearchRow r = meta.getTemplateSimpleRow(false);
Cursor cursor = metaIdIndex.find(session, r, r); r.setValue(0, ValueInt.get(id));
if (cursor.next()) { Cursor cursor = metaIdIndex.find(session, r, r);
Row found = cursor.get(); if (cursor.next()) {
meta.lock(session, true, true); Row found = cursor.get();
meta.removeRow(session, found); meta.lock(session, true, true);
if (isMultiVersion()) { meta.removeRow(session, found);
// TODO this should work without MVCC, but avoid risks at the if (isMultiVersion()) {
// moment // TODO this should work without MVCC, but avoid risks at the
session.log(meta, UndoLogRecord.DELETE, found); // moment
} session.log(meta, UndoLogRecord.DELETE, found);
objectIds.clear(id); }
if (SysProperties.CHECK) { objectIds.clear(id);
checkMetaFree(session, id); if (SysProperties.CHECK) {
checkMetaFree(session, id);
}
} }
} }
if (SysProperties.PAGE_STORE) {
databaseObjects.remove(ObjectUtils.getInteger(id));
}
} }
private HashMap getMap(int type) { private HashMap getMap(int type) {
...@@ -842,9 +846,7 @@ public class Database implements DataHandler { ...@@ -842,9 +846,7 @@ public class Database implements DataHandler {
checkWritingAllowed(); checkWritingAllowed();
} }
obj.getSchema().add(obj); obj.getSchema().add(obj);
if (id > 0 && !starting) { addMeta(session, obj);
addMeta(session, obj);
}
if (obj instanceof TableData) { if (obj instanceof TableData) {
tableMap.put(id, obj); tableMap.put(id, obj);
} }
...@@ -872,9 +874,7 @@ public class Database implements DataHandler { ...@@ -872,9 +874,7 @@ public class Database implements DataHandler {
if (SysProperties.CHECK && map.get(name) != null) { if (SysProperties.CHECK && map.get(name) != null) {
Message.throwInternalError("object already exists"); Message.throwInternalError("object already exists");
} }
if (id > 0 && !starting) { addMeta(session, obj);
addMeta(session, obj);
}
map.put(name, obj); map.put(name, obj);
} }
...@@ -1615,19 +1615,21 @@ public class Database implements DataHandler { ...@@ -1615,19 +1615,21 @@ public class Database implements DataHandler {
removeDatabaseObject(session, comment); removeDatabaseObject(session, comment);
} }
obj.getSchema().remove(obj); obj.getSchema().remove(obj);
String invalid; if (!starting) {
if (SysProperties.OPTIMIZE_DROP_DEPENDENCIES) { String invalid;
Table t = getDependentTable(obj, null); if (SysProperties.OPTIMIZE_DROP_DEPENDENCIES) {
invalid = t == null ? null : t.getSQL(); Table t = getDependentTable(obj, null);
} else { invalid = t == null ? null : t.getSQL();
invalid = getFirstInvalidTable(session); } else {
} invalid = getFirstInvalidTable(session);
if (invalid != null) { }
obj.getSchema().add(obj); if (invalid != null) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_2, new String[] { obj.getSQL(), invalid }); obj.getSchema().add(obj);
throw Message.getSQLException(ErrorCode.CANNOT_DROP_2, new String[] { obj.getSQL(), invalid });
}
obj.removeChildrenAndResources(session);
} }
int id = obj.getId(); int id = obj.getId();
obj.removeChildrenAndResources(session);
removeMeta(session, id); removeMeta(session, id);
if (obj instanceof TableData) { if (obj instanceof TableData) {
tableMap.remove(id); tableMap.remove(id);
...@@ -2154,6 +2156,8 @@ public class Database implements DataHandler { ...@@ -2154,6 +2156,8 @@ public class Database implements DataHandler {
if (add) { if (add) {
objectIds.set(m.getId()); objectIds.set(m.getId());
m.execute(this, systemSession, eventListener); m.execute(this, systemSession, eventListener);
} else {
m.undo(this, systemSession, eventListener);
} }
} }
} }
...@@ -2253,4 +2257,14 @@ public class Database implements DataHandler { ...@@ -2253,4 +2257,14 @@ public class Database implements DataHandler {
} }
} }
/**
* Get a database object.
*
* @param id the object id
* @return the database object
*/
DbObject getDbObject(int id) {
return (DbObject) databaseObjects.get(ObjectUtils.getInteger(id));
}
} }
...@@ -8,12 +8,12 @@ package org.h2.engine; ...@@ -8,12 +8,12 @@ package org.h2.engine;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Comparator; import java.util.Comparator;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.command.Prepared; import org.h2.command.Prepared;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.schema.SchemaObject;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueString; import org.h2.value.ValueString;
...@@ -95,6 +95,37 @@ public class MetaRecord { ...@@ -95,6 +95,37 @@ public class MetaRecord {
} }
} }
/**
* Undo a metadata change.
*
* @param db the database
* @param systemSession the system session
* @param listener the database event listener
*/
void undo(Database db, Session systemSession, DatabaseEventListener listener) throws SQLException {
try {
DbObject obj = db.getDbObject(id);
// null if it was already removed
// (a identity sequence is removed when the table is removed)
if (obj != null) {
if (obj instanceof SchemaObject) {
db.removeSchemaObject(systemSession, (SchemaObject) obj);
} else {
db.removeDatabaseObject(systemSession, obj);
}
}
} catch (Exception e) {
SQLException s = Message.addSQL(Message.convert(e), sql);
db.getTrace(Trace.DATABASE).error(sql, s);
if (listener != null) {
listener.exceptionThrown(s, sql);
// continue startup in this case
} else {
throw s;
}
}
}
public int getId() { public int getId() {
return id; return id;
} }
......
...@@ -31,6 +31,7 @@ public class PageBtreeIndex extends BaseIndex { ...@@ -31,6 +31,7 @@ public class PageBtreeIndex extends BaseIndex {
private TableData tableData; private TableData tableData;
private int headPos; private int headPos;
private long rowCount; private long rowCount;
private boolean needRebuild;
public PageBtreeIndex(TableData table, int id, String indexName, IndexColumn[] columns, public PageBtreeIndex(TableData table, int id, String indexName, IndexColumn[] columns,
IndexType indexType, int headPos) throws SQLException { IndexType indexType, int headPos) throws SQLException {
...@@ -46,14 +47,16 @@ public class PageBtreeIndex extends BaseIndex { ...@@ -46,14 +47,16 @@ public class PageBtreeIndex extends BaseIndex {
} }
this.store = database.getPageStore(); this.store = database.getPageStore();
if (headPos == Index.EMPTY_HEAD) { if (headPos == Index.EMPTY_HEAD) {
// new table // new index
needRebuild = true;
headPos = store.allocatePage(); headPos = store.allocatePage();
PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createDataPage()); PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createDataPage());
store.updateRecord(root, true, root.data); store.updateRecord(root, true, root.data);
} else if (store.isNew()) { int test;
// the system table for a new database // } else if (store.isNew()) {
PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createDataPage()); // // the system table for a new database
store.updateRecord(root, true, root.data); // PageBtreeLeaf root = new PageBtreeLeaf(this, headPos, Page.ROOT, store.createDataPage());
// store.updateRecord(root, true, root.data);
} else { } else {
rowCount = getPage(headPos).getRowCount(); rowCount = getPage(headPos).getRowCount();
int reuseKeysIfManyDeleted; int reuseKeysIfManyDeleted;
...@@ -175,7 +178,7 @@ public class PageBtreeIndex extends BaseIndex { ...@@ -175,7 +178,7 @@ public class PageBtreeIndex extends BaseIndex {
} }
public boolean needRebuild() { public boolean needRebuild() {
return false; return needRebuild;
} }
public void remove(Session session, Row row) throws SQLException { public void remove(Session session, Row row) throws SQLException {
......
...@@ -234,4 +234,8 @@ class PageBtreeLeaf extends PageBtree { ...@@ -234,4 +234,8 @@ class PageBtreeLeaf extends PageBtree {
next.nextPage(cursor, getRow(entryCount - 1)); next.nextPage(cursor, getRow(entryCount - 1));
} }
public String toString() {
return "page[" + getPos() + "] btree leaf table:" + index.getId() + " entries:" + entryCount;
}
} }
...@@ -300,4 +300,9 @@ class PageBtreeNode extends PageBtree { ...@@ -300,4 +300,9 @@ class PageBtreeNode extends PageBtree {
cursor.setCurrent(leaf, 0); cursor.setCurrent(leaf, 0);
} }
public String toString() {
return "page[" + getPos() + "] btree node table:" + index.getId() + " entries:" + entryCount;
}
} }
\ No newline at end of file
...@@ -66,7 +66,7 @@ class PageDataLeaf extends PageData { ...@@ -66,7 +66,7 @@ class PageDataLeaf extends PageData {
if (tableId != index.getId()) { if (tableId != index.getId()) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1,
"page:" + getPageId() + " expected table:" + index.getId() + "page:" + getPageId() + " expected table:" + index.getId() +
"got:" + tableId); " got:" + tableId + " type:" + type);
} }
entryCount = data.readShortInt(); entryCount = data.readShortInt();
offsets = new int[entryCount]; offsets = new int[entryCount];
...@@ -351,4 +351,8 @@ class PageDataLeaf extends PageData { ...@@ -351,4 +351,8 @@ class PageDataLeaf extends PageData {
return data; return data;
} }
public String toString() {
return "page[" + getPos() + "] data leaf table:" + index.getId() + " entries:" + entryCount;
}
} }
...@@ -129,4 +129,8 @@ public class PageDataLeafOverflow extends Record { ...@@ -129,4 +129,8 @@ public class PageDataLeafOverflow extends Record {
store.writePage(getPos(), overflow); store.writePage(getPos(), overflow);
} }
public String toString() {
return "page[" + getPos() + "] data leaf overflow prev:" + previous + " next:" + next;
}
} }
...@@ -26,9 +26,9 @@ import org.h2.store.DataPage; ...@@ -26,9 +26,9 @@ import org.h2.store.DataPage;
*/ */
class PageDataNode extends PageData { class PageDataNode extends PageData {
private final static int ENTRY_START = 15; private static final int ENTRY_START = 15;
private final static int ENTRY_LENGTH = 8; private static final int ENTRY_LENGTH = 8;
/** /**
* The page ids of the children. * The page ids of the children.
...@@ -276,4 +276,8 @@ class PageDataNode extends PageData { ...@@ -276,4 +276,8 @@ class PageDataNode extends PageData {
childPageIds = newChildPageIds; childPageIds = newChildPageIds;
} }
public String toString() {
return "page[" + getPos() + "] data node table:" + index.getId() + " entries:" + entryCount;
}
} }
...@@ -53,13 +53,17 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -53,13 +53,17 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
headPos = store.allocatePage(); headPos = store.allocatePage();
PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage()); PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage());
store.updateRecord(root, true, root.data); store.updateRecord(root, true, root.data);
} else if (store.isNew()) { int test;
// the system table for a new database // } else if (store.isNew()) {
PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage()); // // the system table for a new database
store.updateRecord(root, true, root.data); // PageDataLeaf root = new PageDataLeaf(this, headPos, Page.ROOT, store.createDataPage());
// store.updateRecord(root, true, root.data);
} else { } else {
lastKey = getPage(headPos).getLastKey(); PageData root = getPage(headPos);
rowCount = getPage(headPos).getRowCount(); lastKey = root.getLastKey();
rowCount = root.getRowCount();
// could have been created before, but never committed
store.updateRecord(root, false, null);
int reuseKeysIfManyDeleted; int reuseKeysIfManyDeleted;
} }
this.headPos = headPos; this.headPos = headPos;
...@@ -127,6 +131,10 @@ public class PageScanIndex extends BaseIndex implements RowIndex { ...@@ -127,6 +131,10 @@ public class PageScanIndex extends BaseIndex implements RowIndex {
PageData getPage(int id) throws SQLException { PageData getPage(int id) throws SQLException {
Record rec = store.getRecord(id); Record rec = store.getRecord(id);
if (rec != null) { if (rec != null) {
if (rec instanceof PageDataLeafOverflow) {
int test;
System.out.println("stop");
}
return (PageData) rec; return (PageData) rec;
} }
DataPage data = store.readPage(id); DataPage data = store.readPage(id);
......
/* /*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License, Version
* Version 1.0, and under the Eclipse Public License, Version 1.0 * 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html). Initial Developer: H2 Group
* Initial Developer: H2 Group
*/ */
package org.h2.store; package org.h2.store;
...@@ -10,82 +9,118 @@ import java.sql.SQLException; ...@@ -10,82 +9,118 @@ import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.index.Page; import org.h2.index.Page;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.util.IntArray; import org.h2.util.BitField;
/** /**
* The list of free pages of a page store. * The list of free pages of a page store. The format of a free list trunk page
* The format of a free list trunk page is: * is:
* <ul><li>0-3: parent page id (always 0) * <ul>
* </li><li>4-4: page type * <li>0-3: parent page id (always 0)</li>
* </li><li>5-8: the next page (if there are more) or number of entries * <li>4-4: page type</li>
* </li><li>9-remainder: data (4 bytes each entry) * <li>5-remainder: data</li>
* </li></ul> * </ul>
*/ */
public class PageFreeList extends Record { public class PageFreeList extends Record {
private static final int DATA_START = 5;
private final PageStore store; private final PageStore store;
private final DataPage data; private final BitField used = new BitField();
private final IntArray array = new IntArray(); private final int firstAddressed;
private int nextPage; private final int pageCount;
private final int nextPage;
private boolean full;
private DataPage data;
PageFreeList(PageStore store, int pageId, int nextPage) { PageFreeList(PageStore store, int pageId, int firstAddressed) {
setPos(pageId); setPos(pageId);
this.data = store.createDataPage();
this.store = store; this.store = store;
this.nextPage = nextPage; this.firstAddressed = firstAddressed;
pageCount = (store.getPageSize() - DATA_START) * 8;
for (int i = firstAddressed; i <= pageId; i++) {
used.set(getAddress(i));
}
nextPage = firstAddressed + pageCount;
}
private int getAddress(int pageId) {
return pageId - firstAddressed;
} }
/** /**
* Allocate a page from the free list. * Allocate a page from the free list.
* *
* @return the page * @return the page, or -1 if all pages are used
*/ */
int allocate() throws SQLException { int allocate() throws SQLException {
if (full) {
PageFreeList next = getNext();
if (next == null) {
return -1;
}
return next.allocate();
}
int free = used.nextClearBit(0);
if (free > pageCount) {
full = true;
return allocate();
}
used.set(free);
store.updateRecord(this, true, data); store.updateRecord(this, true, data);
int size = array.size(); return free + firstAddressed;
if (size > 0) { }
int x = array.get(size - 1);
array.remove(size - 1); /**
return x; * Allocate a page at the end of the file
*
* @param min the minimum page number
* @return the page id
*/
int allocateAtEnd(int min) throws SQLException {
int pos = Math.max(min, getLastUsed() + 1);
return allocate(pos);
}
public int getLastUsed() throws SQLException {
if (nextPage < store.getPageCount()) {
PageFreeList next = getNext();
return next.getLastUsed();
} }
store.removeRecord(getPos()); return used.getLastSetBit() + firstAddressed;
// no more free pages in this list:
// set the next page (may be 0, meaning no free pages)
store.setFreeListRootPage(nextPage, true, 0);
// and then return the page itself
return getPos();
} }
private int getMaxSize() { private PageFreeList getNext() throws SQLException {
return (store.getPageSize() - 9) / DataPage.LENGTH_INT; PageFreeList next = (PageFreeList) store.getRecord(nextPage);
if (next == null) {
if (nextPage < store.getPageCount()) {
next = new PageFreeList(store, nextPage, nextPage);
next.read();
store.updateRecord(next, false, null);
}
}
return next;
} }
/** /**
* Read the page from the disk. * Mark a page as used.
*
* @param pos the page id
* @return the page id, or -1
*/ */
void read() throws SQLException { int allocate(int pos) throws SQLException {
data.reset(); if (pos - firstAddressed > pageCount) {
store.readPage(getPos(), data); PageFreeList next = getNext();
int p = data.readInt(); if (next == null) {
int t = data.readByte(); return -1;
boolean last = (t & Page.FLAG_LAST) != 0; }
t &= ~Page.FLAG_LAST; return next.allocate(pos);
if (t != Page.TYPE_FREE_LIST || p != 0) {
throw Message.getSQLException(
ErrorCode.FILE_CORRUPTED_1,
"pos:" + getPos() + " type:" + t + " parent:" + p +
" expected type:" + Page.TYPE_FREE_LIST);
}
int size;
if (last) {
nextPage = 0;
size = data.readInt();
} else { } else {
nextPage = data.readInt(); int idx = pos - firstAddressed;
size = getMaxSize(); if (idx >= 0 && !used.get(idx)) {
} used.set(pos - firstAddressed);
for (int i = 0; i < size; i++) { store.updateRecord(this, true, data);
array.add(data.readInt()); }
return pos;
} }
} }
...@@ -95,15 +130,28 @@ public class PageFreeList extends Record { ...@@ -95,15 +130,28 @@ public class PageFreeList extends Record {
* @param pageId the page id to add * @param pageId the page id to add
*/ */
void free(int pageId) throws SQLException { void free(int pageId) throws SQLException {
full = false;
used.clear(pageId - firstAddressed);
store.updateRecord(this, true, data); store.updateRecord(this, true, data);
if (array.size() < getMaxSize()) { }
array.add(pageId);
} else { /**
// this page is full: * Read the page from the disk.
// the freed page is the next list */
this.nextPage = pageId; void read() throws SQLException {
// set the next page data = store.createDataPage();
store.setFreeListRootPage(pageId, false, getPos()); store.readPage(getPos(), data);
int p = data.readInt();
int t = data.readByte();
if (t == Page.TYPE_EMPTY) {
return;
}
if (t != Page.TYPE_FREE_LIST || p != 0) {
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, "pos:" + getPos() + " type:" + t + " parent:" + p
+ " expected type:" + Page.TYPE_FREE_LIST);
}
for (int i = 0; i < pageCount; i += 8) {
used.setByte(i, data.readByte());
} }
} }
...@@ -112,20 +160,12 @@ public class PageFreeList extends Record { ...@@ -112,20 +160,12 @@ public class PageFreeList extends Record {
} }
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
data.reset(); data = store.createDataPage();
data.writeInt(0); data.writeInt(0);
int type = Page.TYPE_FREE_LIST; int type = Page.TYPE_FREE_LIST;
if (nextPage == 0) {
type |= Page.FLAG_LAST;
}
data.writeByte((byte) type); data.writeByte((byte) type);
if (nextPage != 0) { for (int i = 0; i < pageCount; i += 8) {
data.writeInt(nextPage); data.writeByte((byte) used.getByte(i));
} else {
data.writeInt(array.size());
}
for (int i = 0; i < array.size(); i++) {
data.writeInt(array.get(i));
} }
store.writePage(getPos(), data); store.writePage(getPos(), data);
} }
......
...@@ -12,6 +12,7 @@ import java.sql.SQLException; ...@@ -12,6 +12,7 @@ import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.index.Page; import org.h2.index.Page;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace;
/** /**
* An output stream that writes into a page store. * An output stream that writes into a page store.
...@@ -25,6 +26,7 @@ import org.h2.message.Message; ...@@ -25,6 +26,7 @@ import org.h2.message.Message;
public class PageInputStream extends InputStream { public class PageInputStream extends InputStream {
private PageStore store; private PageStore store;
private final Trace trace;
private int parentPage; private int parentPage;
private int type; private int type;
private int nextPage; private int nextPage;
...@@ -35,6 +37,7 @@ public class PageInputStream extends InputStream { ...@@ -35,6 +37,7 @@ public class PageInputStream extends InputStream {
public PageInputStream(PageStore store, int parentPage, int headPage, int type) { public PageInputStream(PageStore store, int parentPage, int headPage, int type) {
this.store = store; this.store = store;
this.trace = store.getTrace();
this.parentPage = parentPage; this.parentPage = parentPage;
this.type = type; this.type = type;
nextPage = headPage; nextPage = headPage;
...@@ -108,6 +111,9 @@ public class PageInputStream extends InputStream { ...@@ -108,6 +111,9 @@ public class PageInputStream extends InputStream {
nextPage = page.readInt(); nextPage = page.readInt();
remaining = store.getPageSize() - page.length(); remaining = store.getPageSize() - page.length();
} }
if (trace.isDebugEnabled()) {
trace.debug("pageIn.readPage " + parentPage + " next:" + nextPage);
}
} catch (SQLException e) { } catch (SQLException e) {
throw Message.convertToIOException(e); throw Message.convertToIOException(e);
} }
......
...@@ -74,6 +74,7 @@ public class PageLog { ...@@ -74,6 +74,7 @@ public class PageLog {
private DataPage data; private DataPage data;
private long operation; private long operation;
private BitField undo = new BitField(); private BitField undo = new BitField();
private int[] reservedPages = new int[2];
PageLog(PageStore store, int firstPage) { PageLog(PageStore store, int firstPage) {
this.store = store; this.store = store;
...@@ -215,15 +216,29 @@ e.printStackTrace(); ...@@ -215,15 +216,29 @@ e.printStackTrace();
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("log undo " + pageId); trace.debug("log undo " + pageId);
} }
undo.set(pageId);
reservePages(2);
out.write(UNDO); out.write(UNDO);
out.writeInt(pageId); out.writeInt(pageId);
out.write(page.getBytes(), 0, store.getPageSize()); out.write(page.getBytes(), 0, store.getPageSize());
undo.set(pageId);
} catch (IOException e) { } catch (IOException e) {
throw Message.convertIOException(e, null); throw Message.convertIOException(e, null);
} }
} }
private void reservePages(int pageCount) throws SQLException {
int testIfRequired;
if (pageCount > reservedPages.length) {
reservedPages = new int[pageCount];
}
for (int i = 0; i < pageCount; i++) {
reservedPages[i] = store.allocatePage();
}
for (int i = 0; i < pageCount; i++) {
store.freePage(reservedPages[i]);
}
}
/** /**
* Mark a committed transaction. * Mark a committed transaction.
* *
...@@ -232,6 +247,7 @@ e.printStackTrace(); ...@@ -232,6 +247,7 @@ e.printStackTrace();
void commit(Session session) throws SQLException { void commit(Session session) throws SQLException {
try { try {
trace.debug("log commit"); trace.debug("log commit");
reservePages(1);
out.write(COMMIT); out.write(COMMIT);
out.writeInt(session.getId()); out.writeInt(session.getId());
if (store.getDatabase().getLog().getFlushOnEachCommit()) { if (store.getDatabase().getLog().getFlushOnEachCommit()) {
...@@ -259,13 +275,17 @@ e.printStackTrace(); ...@@ -259,13 +275,17 @@ e.printStackTrace();
int todoLogPosShouldBeLong; int todoLogPosShouldBeLong;
session.addLogPos(0, (int) operation); session.addLogPos(0, (int) operation);
row.setLastLog(0, (int) operation); row.setLastLog(0, (int) operation);
data.reset();
int todoWriteIntoOutputDirectly;
row.write(data);
reservePages(1 + data.length() / store.getPageSize());
out.write(add ? ADD : REMOVE); out.write(add ? ADD : REMOVE);
out.writeInt(session.getId()); out.writeInt(session.getId());
out.writeInt(tableId); out.writeInt(tableId);
out.writeInt(row.getPos()); out.writeInt(row.getPos());
data.reset();
int todoWriteIntoOutputDirectly;
row.write(data);
out.writeInt(data.length()); out.writeInt(data.length());
out.write(data.getBytes(), 0, data.length()); out.write(data.getBytes(), 0, data.length());
} catch (IOException e) { } catch (IOException e) {
......
...@@ -13,12 +13,17 @@ import java.util.HashMap; ...@@ -13,12 +13,17 @@ import java.util.HashMap;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.index.Page; import org.h2.index.IndexType;
import org.h2.index.PageScanIndex;
import org.h2.log.SessionState; import org.h2.log.SessionState;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.util.Cache; import org.h2.util.Cache;
import org.h2.util.Cache2Q; import org.h2.util.Cache2Q;
import org.h2.util.CacheLRU; import org.h2.util.CacheLRU;
...@@ -27,6 +32,7 @@ import org.h2.util.CacheWriter; ...@@ -27,6 +32,7 @@ import org.h2.util.CacheWriter;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils; import org.h2.util.ObjectUtils;
import org.h2.value.Value;
/** /**
* This class represents a file that is organized as a number of pages. The * This class represents a file that is organized as a number of pages. The
...@@ -92,7 +98,7 @@ public class PageStore implements CacheWriter { ...@@ -92,7 +98,7 @@ public class PageStore implements CacheWriter {
*/ */
public static final int LOG_COUNT = 2; public static final int LOG_COUNT = 2;
private static final int INCREMENT_PAGES = 128; static final int INCREMENT_PAGES = 128;
private static final int READ_VERSION = 0; private static final int READ_VERSION = 0;
private static final int WRITE_VERSION = 0; private static final int WRITE_VERSION = 0;
...@@ -108,6 +114,7 @@ public class PageStore implements CacheWriter { ...@@ -108,6 +114,7 @@ public class PageStore implements CacheWriter {
private int pageSizeShift; private int pageSizeShift;
private int systemRootPageId; private int systemRootPageId;
private int freeListRootPageId; private int freeListRootPageId;
private int lastUsedPage;
private int activeLog; private int activeLog;
private int[] logRootPageIds = new int[LOG_COUNT]; private int[] logRootPageIds = new int[LOG_COUNT];
...@@ -124,18 +131,6 @@ public class PageStore implements CacheWriter { ...@@ -124,18 +131,6 @@ public class PageStore implements CacheWriter {
*/ */
private int pageCount; private int pageCount;
/**
* The last page that is in use.
*/
private int lastUsedPage;
/**
* Number of free pages in the free list.
* This does not include empty pages at the end of the file
* (after the last used page).
*/
private int freePageCount;
/** /**
* The transaction logs. * The transaction logs.
*/ */
...@@ -146,6 +141,9 @@ public class PageStore implements CacheWriter { ...@@ -146,6 +141,9 @@ public class PageStore implements CacheWriter {
*/ */
private boolean isNew; private boolean isNew;
private DataPage pageTable;
private PageScanIndex pageIndex;
/** /**
* Create a new page store object. * Create a new page store object.
* *
...@@ -160,7 +158,7 @@ public class PageStore implements CacheWriter { ...@@ -160,7 +158,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)) {
...@@ -205,34 +203,42 @@ public class PageStore implements CacheWriter { ...@@ -205,34 +203,42 @@ public class PageStore implements CacheWriter {
fileLength = file.length(); fileLength = file.length();
pageCount = (int) (fileLength / pageSize); pageCount = (int) (fileLength / pageSize);
initLogs(); initLogs();
lastUsedPage = pageCount - 1;
} else { } else {
isNew = true; isNew = true;
setPageSize(PAGE_SIZE_DEFAULT); setPageSize(PAGE_SIZE_DEFAULT);
file = database.openFile(fileName, accessMode, false); file = database.openFile(fileName, accessMode, false);
systemRootPageId = 1; systemRootPageId = 1;
freeListRootPageId = 2; freeListRootPageId = 2;
PageFreeList free = new PageFreeList(this, freeListRootPageId, 0); pageCount = 3 + LOG_COUNT;
updateRecord(free, false, null); increaseFileSize(INCREMENT_PAGES - pageCount);
PageFreeList list = getFreeList();
for (int i = 0; i < LOG_COUNT; i++) { for (int i = 0; i < LOG_COUNT; i++) {
logRootPageIds[i] = 3 + i; logRootPageIds[i] = 3 + i;
} }
lastUsedPage = 3 + LOG_COUNT;
int todoShouldBeOneMoreStartWith0; int todoShouldBeOneMoreStartWith0;
pageCount = lastUsedPage;
increaseFileSize(INCREMENT_PAGES - pageCount);
writeHeader(); writeHeader();
initLogs(); initLogs();
getLog().openForWriting(0); getLog().openForWriting(0);
switchLogIfPossible(); switchLogIfPossible();
getLog().flush(); getLog().flush();
} }
lastUsedPage = getFreeList().getLastUsed() + 1;
} catch (SQLException e) { } catch (SQLException e) {
close(); close();
throw e; throw e;
} }
} }
private void openPageIndex() throws SQLException {
ObjectArray cols = new ObjectArray();
cols.add(new Column("HEAD", Value.INT));
cols.add(new Column("TABLE", Value.INT));
cols.add(new Column("COLUMNS", Value.STRING));
int headPos = getSystemRootPageId();
// pageTable = database.getMainSchema().createTable("PAGE_INDEX", 0, cols, true, false, headPos);
// pageIndex = (PageScanIndex) pageTable.getScanIndex(database.getSystemSession());
}
private void initLogs() { private void initLogs() {
for (int i = 0; i < LOG_COUNT; i++) { for (int i = 0; i < LOG_COUNT; i++) {
logs[i] = new PageLog(this, logRootPageIds[i]); logs[i] = new PageLog(this, logRootPageIds[i]);
...@@ -260,7 +266,8 @@ public class PageStore implements CacheWriter { ...@@ -260,7 +266,8 @@ public class PageStore implements CacheWriter {
switchLogIfPossible(); switchLogIfPossible();
int todoWriteDeletedPages; int todoWriteDeletedPages;
} }
pageCount = lastUsedPage + 1; int pageCount = getFreeList().getLastUsed() + 1;
trace.debug("pageCount:" + pageCount);
file.setLength(pageSize * pageCount); file.setLength(pageSize * pageCount);
} }
...@@ -272,7 +279,6 @@ public class PageStore implements CacheWriter { ...@@ -272,7 +279,6 @@ public class PageStore implements CacheWriter {
int todoCanOnlyReuseAfterLoggedChangesAreWritten; int todoCanOnlyReuseAfterLoggedChangesAreWritten;
getLog().openForWriting(id + 1); getLog().openForWriting(id + 1);
// Session[] sessions = database.getSessions(true); // Session[] sessions = database.getSessions(true);
// int firstUncommittedLog = getLog().getId(); // int firstUncommittedLog = getLog().getId();
// int firstUncommittedPos = getLog().getPos(); // int firstUncommittedPos = getLog().getPos();
...@@ -326,15 +332,6 @@ public class PageStore implements CacheWriter { ...@@ -326,15 +332,6 @@ public class PageStore implements CacheWriter {
} }
} }
/**
* Check if this page store was just created.
*
* @return true if it was
*/
public boolean isNew() {
return isNew;
}
/** /**
* Set the page size. The size must be a power of two. This method must be * Set the page size. The size must be a power of two. This method must be
* called before opening. * called before opening.
...@@ -405,7 +402,7 @@ public class PageStore implements CacheWriter { ...@@ -405,7 +402,7 @@ public class PageStore implements CacheWriter {
synchronized (database) { synchronized (database) {
Record record = (Record) obj; Record record = (Record) obj;
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("writeBack " + record.getPos() + ":" + record); trace.debug("writeBack " + record);
} }
int todoRemoveParameter; int todoRemoveParameter;
record.write(null); record.write(null);
...@@ -421,13 +418,15 @@ public class PageStore implements CacheWriter { ...@@ -421,13 +418,15 @@ public class PageStore implements CacheWriter {
* @param old the old data (if known) * @param old the old data (if known)
*/ */
public void updateRecord(Record record, boolean logUndo, DataPage old) throws SQLException { public void updateRecord(Record record, boolean logUndo, DataPage old) throws SQLException {
int todoLogHeaderPageAsWell;
if (trace.isDebugEnabled()) {
trace.debug("updateRecord " + record.getPos() + " " + record.toString());
}
synchronized (database) { synchronized (database) {
if (trace.isDebugEnabled()) {
if (!record.isChanged()) {
trace.debug("updateRecord " + record.toString());
}
}
record.setChanged(true); record.setChanged(true);
int pos = record.getPos(); int pos = record.getPos();
getFreeList().allocate(pos);
cache.update(pos, record); cache.update(pos, record);
if (logUndo && !recoveryRunning) { if (logUndo && !recoveryRunning) {
if (old == null) { if (old == null) {
...@@ -447,6 +446,16 @@ public class PageStore implements CacheWriter { ...@@ -447,6 +446,16 @@ public class PageStore implements CacheWriter {
return allocatePage(false); return allocatePage(false);
} }
private PageFreeList getFreeList() throws SQLException {
PageFreeList free = (PageFreeList) cache.find(freeListRootPageId);
if (free == null) {
free = new PageFreeList(this, freeListRootPageId, 3 + LOG_COUNT);
free.read();
cache.put(free);
}
return free;
}
/** /**
* Allocate a page. * Allocate a page.
* *
...@@ -454,21 +463,20 @@ public class PageStore implements CacheWriter { ...@@ -454,21 +463,20 @@ public class PageStore implements CacheWriter {
* @return the page id * @return the page id
*/ */
public int allocatePage(boolean atEnd) throws SQLException { public int allocatePage(boolean atEnd) throws SQLException {
if (freePageCount > 0 && !atEnd) { PageFreeList list = getFreeList();
if (freeListRootPageId == 0) { int id;
Message.throwInternalError(); while (true) {
} id = atEnd ? list.allocateAtEnd(++lastUsedPage) : list.allocate();
PageFreeList free = (PageFreeList) cache.find(freeListRootPageId); if (id < 0) {
if (free == null) { increaseFileSize(INCREMENT_PAGES);
free = new PageFreeList(this, freeListRootPageId, 0); } else {
free.read(); break;
} }
int id = free.allocate();
freePageCount--;
return id;
} }
int id = ++lastUsedPage; if (trace.isDebugEnabled()) {
if (id >= pageCount) { trace.debug("allocated " + id + " atEnd:" + atEnd);
}
if (id > pageCount) {
increaseFileSize(INCREMENT_PAGES); increaseFileSize(INCREMENT_PAGES);
} }
return id; return id;
...@@ -490,15 +498,11 @@ public class PageStore implements CacheWriter { ...@@ -490,15 +498,11 @@ public class PageStore implements CacheWriter {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
trace.debug("freePage " + pageId); trace.debug("freePage " + pageId);
} }
freePageCount++;
PageFreeList free;
cache.remove(pageId); cache.remove(pageId);
free = (PageFreeList) cache.find(freeListRootPageId); getFreeList().free(pageId);
if (free == null) { if (recoveryRunning) {
free = new PageFreeList(this, freeListRootPageId, 0); writePage(pageId, createDataPage());
free.read();
} }
free.free(pageId);
} }
/** /**
...@@ -618,14 +622,24 @@ public class PageStore implements CacheWriter { ...@@ -618,14 +622,24 @@ public class PageStore implements CacheWriter {
} }
/** /**
* Run the recovery process. There are two recovery stages: first (undo is * Run recovery.
*/
public void recover() throws SQLException {
recover(true);
openPageIndex();
recover(false);
checkpoint();
}
/**
* Run one recovery stage. There are two recovery stages: first (undo is
* true) only the undo steps are run (restoring the state before the last * true) only the undo steps are run (restoring the state before the last
* checkpoint). In the second stage (undo is false) the committed operations * checkpoint). In the second stage (undo is false) the committed operations
* are re-applied. * are re-applied.
* *
* @param undo true if the undo step should be run * @param undo true if the undo step should be run
*/ */
public void recover(boolean undo) throws SQLException { private void recover(boolean undo) throws SQLException {
trace.debug("log recover"); trace.debug("log recover");
try { try {
recoveryRunning = true; recoveryRunning = true;
...@@ -638,8 +652,14 @@ public class PageStore implements CacheWriter { ...@@ -638,8 +652,14 @@ public class PageStore implements CacheWriter {
} }
} }
for (int i = 0; i < LOG_COUNT; i++) { for (int i = 0; i < LOG_COUNT; i++) {
// start with the oldest log file int j;
int j = (activeLog + 1 + i) % LOG_COUNT; if (undo) {
// undo: start with the newest file and go backward
j = Math.abs(activeLog - i) % LOG_COUNT;
} else {
// redo: start with the oldest log file
j = (activeLog + 1 + i) % LOG_COUNT;
}
logs[j].recover(undo); logs[j].recover(undo);
} }
if (!undo) { if (!undo) {
...@@ -647,18 +667,16 @@ public class PageStore implements CacheWriter { ...@@ -647,18 +667,16 @@ public class PageStore implements CacheWriter {
int todoProbablyStillRequiredForTwoPhaseCommit; int todoProbablyStillRequiredForTwoPhaseCommit;
sessionStates = new HashMap(); sessionStates = new HashMap();
} }
} catch (SQLException e) {
int test;
e.printStackTrace();
throw e;
} catch (RuntimeException e) {
int test;
e.printStackTrace();
throw e;
} finally { } finally {
recoveryRunning = false; recoveryRunning = false;
// re-calculate the last used page
while (true) {
DataPage page = readPage(lastUsedPage);
page.readInt();
int type = page.readByte();
if (type != Page.TYPE_EMPTY) {
break;
}
lastUsedPage--;
}
} }
trace.debug("log recover done"); trace.debug("log recover done");
} }
...@@ -735,4 +753,17 @@ public class PageStore implements CacheWriter { ...@@ -735,4 +753,17 @@ public class PageStore implements CacheWriter {
return state.isCommitted(logId, pos); return state.isCommitted(logId, pos);
} }
public int getMetaTableHeadPos() {
int test;
return 0;
}
// public int getMetaTableHeadPos() {
// SearchRow r = pageIndex.getTemplateSimpleRow(false);
//
// return pageIndex.find(database.getSystemSession(), first, last)
// // TODO Auto-generated method stub
// return 0;
// }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论