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

Multiple page store bugfixes.

上级 d66da596
...@@ -234,9 +234,9 @@ public abstract class PageBtree extends Page { ...@@ -234,9 +234,9 @@ public abstract class PageBtree extends Page {
abstract SearchRow remove(SearchRow row) throws SQLException; abstract SearchRow remove(SearchRow row) throws SQLException;
/** /**
* Free up all child pages. * Free this page and all child pages.
*/ */
abstract void freeChildren() throws SQLException; abstract void freeRecursive() throws SQLException;
/** /**
* Ensure all rows are read in memory. * Ensure all rows are read in memory.
......
...@@ -229,7 +229,7 @@ public class PageBtreeIndex extends PageIndex { ...@@ -229,7 +229,7 @@ public class PageBtreeIndex extends PageIndex {
trace.debug("remove"); trace.debug("remove");
} }
removeAllRows(); removeAllRows();
store.free(rootPageId, true); store.free(rootPageId);
store.removeMeta(this, session); store.removeMeta(this, session);
} }
...@@ -246,8 +246,7 @@ public class PageBtreeIndex extends PageIndex { ...@@ -246,8 +246,7 @@ public class PageBtreeIndex extends PageIndex {
private void removeAllRows() throws SQLException { private void removeAllRows() throws SQLException {
PageBtree root = getPage(rootPageId); PageBtree root = getPage(rootPageId);
store.logUndo(root, root.data); root.freeRecursive();
root.freeChildren();
root = PageBtreeLeaf.create(this, rootPageId, PageBtree.ROOT); root = PageBtreeLeaf.create(this, rootPageId, PageBtree.ROOT);
store.removeRecord(rootPageId); store.removeRecord(rootPageId);
store.update(root); store.update(root);
......
...@@ -222,8 +222,9 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -222,8 +222,9 @@ public class PageBtreeLeaf extends PageBtree {
return null; return null;
} }
void freeChildren() { void freeRecursive() throws SQLException {
// nothing to do index.getPageStore().logUndo(this, data);
index.getPageStore().free(getPos());
} }
int getRowCount() { int getRowCount() {
...@@ -240,7 +241,6 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -240,7 +241,6 @@ public class PageBtreeLeaf extends PageBtree {
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
write(); write();
index.getPageStore().checkUndo(getPos());
index.getPageStore().writePage(getPos(), data); index.getPageStore().writePage(getPos(), data);
} }
...@@ -340,7 +340,7 @@ public class PageBtreeLeaf extends PageBtree { ...@@ -340,7 +340,7 @@ public class PageBtreeLeaf extends PageBtree {
PageBtreeNode p = (PageBtreeNode) store.getPage(parentPageId); PageBtreeNode p = (PageBtreeNode) store.getPage(parentPageId);
p.moveChild(getPos(), newPos); p.moveChild(getPos(), newPos);
} }
store.free(getPos(), true); store.free(getPos());
} }
} }
...@@ -318,7 +318,7 @@ public class PageBtreeNode extends PageBtree { ...@@ -318,7 +318,7 @@ public class PageBtreeNode extends PageBtree {
return null; return null;
} else if (last == row) { } else if (last == row) {
// this child is now empty // this child is now empty
index.getPageStore().free(page.getPos(), true); index.getPageStore().free(page.getPos());
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 row; return row;
...@@ -390,7 +390,6 @@ public class PageBtreeNode extends PageBtree { ...@@ -390,7 +390,6 @@ public class PageBtreeNode extends PageBtree {
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
check(); check();
write(); write();
index.getPageStore().checkUndo(getPos());
index.getPageStore().writePage(getPos(), data); index.getPageStore().writePage(getPos(), data);
} }
...@@ -421,12 +420,10 @@ public class PageBtreeNode extends PageBtree { ...@@ -421,12 +420,10 @@ public class PageBtreeNode extends PageBtree {
written = true; written = true;
} }
void freeChildren() throws SQLException { void freeRecursive() throws SQLException {
for (int i = 0; i <= entryCount; i++) { index.getPageStore().logUndo(this, data);
int childPageId = childPageIds[i]; for (int childPageId : childPageIds) {
PageBtree child = index.getPage(childPageId); index.getPage(childPageId).freeRecursive();
index.getPageStore().free(childPageId, false);
child.freeChildren();
} }
} }
...@@ -542,12 +539,12 @@ public class PageBtreeNode extends PageBtree { ...@@ -542,12 +539,12 @@ public class PageBtreeNode extends PageBtree {
PageBtreeNode p = (PageBtreeNode) store.getPage(parentPageId); PageBtreeNode p = (PageBtreeNode) store.getPage(parentPageId);
p.moveChild(getPos(), newPos); p.moveChild(getPos(), newPos);
} }
for (int i = 0; i < childPageIds.length; i++) { for (int childPageId : childPageIds) {
PageBtree p = (PageBtree) store.getPage(childPageIds[i]); PageBtree p = index.getPage(childPageId);
p.setParentPageId(newPos); p.setParentPageId(newPos);
store.update(p); store.update(p);
} }
store.free(getPos(), true); store.free(getPos());
} }
/** /**
......
...@@ -198,9 +198,9 @@ abstract class PageData extends Page { ...@@ -198,9 +198,9 @@ abstract class PageData extends Page {
abstract boolean remove(long key) throws SQLException; abstract boolean remove(long key) throws SQLException;
/** /**
* Free up all child pages. * Free this page and all child pages.
*/ */
abstract void freeChildren() throws SQLException; abstract void freeRecursive() throws SQLException;
/** /**
* Get the row for the given key. * Get the row for the given key.
......
...@@ -12,6 +12,7 @@ import java.util.HashMap; ...@@ -12,6 +12,7 @@ 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.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;
...@@ -188,7 +189,11 @@ public class PageDataIndex extends PageIndex implements RowIndex { ...@@ -188,7 +189,11 @@ public class PageDataIndex extends PageIndex implements RowIndex {
* @return the page * @return the page
*/ */
PageDataOverflow getPageOverflow(int id) throws SQLException { PageDataOverflow getPageOverflow(int id) throws SQLException {
return (PageDataOverflow) store.getPage(id); Page p = store.getPage(id);
if (p instanceof PageDataOverflow) {
return (PageDataOverflow) p;
}
throw Message.getSQLException(ErrorCode.FILE_CORRUPTED_1, p.toString());
} }
/** /**
...@@ -317,7 +322,7 @@ public class PageDataIndex extends PageIndex implements RowIndex { ...@@ -317,7 +322,7 @@ public class PageDataIndex extends PageIndex implements RowIndex {
trace.debug(this + " remove"); trace.debug(this + " remove");
} }
removeAllRows(); removeAllRows();
store.free(rootPageId, true); store.free(rootPageId);
store.removeMeta(this, session); store.removeMeta(this, session);
} }
...@@ -338,8 +343,7 @@ public class PageDataIndex extends PageIndex implements RowIndex { ...@@ -338,8 +343,7 @@ public class PageDataIndex extends PageIndex implements RowIndex {
private void removeAllRows() throws SQLException { private void removeAllRows() throws SQLException {
PageData root = getPage(rootPageId, 0); PageData root = getPage(rootPageId, 0);
store.logUndo(root, root.data); root.freeRecursive();
root.freeChildren();
root = PageDataLeaf.create(this, rootPageId, PageData.ROOT); root = PageDataLeaf.create(this, rootPageId, PageData.ROOT);
store.removeRecord(rootPageId); store.removeRecord(rootPageId);
store.update(root); store.update(root);
......
...@@ -143,6 +143,14 @@ public class PageDataLeaf extends PageData { ...@@ -143,6 +143,14 @@ public class PageDataLeaf extends PageData {
return size; return size;
} }
private int findInsertionPoint(long key) throws SQLException {
int x = find(key);
if (x < keys.length && keys[x] == key) {
throw index.getDuplicateKeyException();
}
return x;
}
int addRowTry(Row row) throws SQLException { int addRowTry(Row row) throws SQLException {
index.getPageStore().logUndo(this, data); index.getPageStore().logUndo(this, data);
int rowLength = getRowLength(row); int rowLength = getRowLength(row);
...@@ -150,7 +158,7 @@ public class PageDataLeaf extends PageData { ...@@ -150,7 +158,7 @@ public class PageDataLeaf extends PageData {
int last = entryCount == 0 ? pageSize : offsets[entryCount - 1]; int last = entryCount == 0 ? pageSize : offsets[entryCount - 1];
int keyOffsetPairLen = 2 + data.getVarLongLen(row.getKey()); int keyOffsetPairLen = 2 + data.getVarLongLen(row.getKey());
if (entryCount > 0 && last - rowLength < start + keyOffsetPairLen) { if (entryCount > 0 && last - rowLength < start + keyOffsetPairLen) {
int x = find(row.getKey()); int x = findInsertionPoint(row.getKey());
if (entryCount > 1) { if (entryCount > 1) {
if (entryCount < 5) { if (entryCount < 5) {
// required, otherwise the index doesn't work correctly // required, otherwise the index doesn't work correctly
...@@ -177,10 +185,7 @@ public class PageDataLeaf extends PageData { ...@@ -177,10 +185,7 @@ public class PageDataLeaf extends PageData {
x = 0; x = 0;
} else { } else {
readAllRows(); readAllRows();
x = find(row.getKey()); x = findInsertionPoint(row.getKey());
if (x < keys.length && keys[x] == row.getKey()) {
throw index.getDuplicateKeyException();
}
System.arraycopy(offsets, 0, newOffsets, 0, x); System.arraycopy(offsets, 0, newOffsets, 0, x);
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);
...@@ -262,7 +267,7 @@ public class PageDataLeaf extends PageData { ...@@ -262,7 +267,7 @@ public class PageDataLeaf extends PageData {
if (entryCount < 0) { if (entryCount < 0) {
Message.throwInternalError(); Message.throwInternalError();
} }
freeChildren(); freeOverflow();
firstOverflowPageId = 0; firstOverflowPageId = 0;
overflowRowSize = 0; overflowRowSize = 0;
rowRef = null; rowRef = null;
...@@ -320,10 +325,6 @@ public class PageDataLeaf extends PageData { ...@@ -320,10 +325,6 @@ public class PageDataLeaf extends PageData {
int next = firstOverflowPageId; int next = firstOverflowPageId;
do { do {
PageDataOverflow page = index.getPageOverflow(next); PageDataOverflow page = index.getPageOverflow(next);
if (page == null) {
page = index.getPageOverflow(next);
System.out.println("stop!");
}
next = page.readInto(buff); next = page.readInto(buff);
} while (next != 0); } while (next != 0);
overflowRowSize = pageSize + buff.length(); overflowRowSize = pageSize + buff.length();
...@@ -394,7 +395,7 @@ if (page == null) { ...@@ -394,7 +395,7 @@ if (page == null) {
} }
index.getPageStore().logUndo(this, data); index.getPageStore().logUndo(this, data);
if (entryCount == 1) { if (entryCount == 1) {
freeChildren(); freeRecursive();
return true; return true;
} }
removeRow(i); removeRow(i);
...@@ -402,13 +403,18 @@ if (page == null) { ...@@ -402,13 +403,18 @@ if (page == null) {
return false; return false;
} }
void freeChildren() throws SQLException { void freeRecursive() throws SQLException {
index.getPageStore().logUndo(this, data);
index.getPageStore().free(getPos());
freeOverflow();
}
void freeOverflow() throws SQLException {
if (firstOverflowPageId != 0) { if (firstOverflowPageId != 0) {
PageStore store = index.getPageStore();
int next = firstOverflowPageId; int next = firstOverflowPageId;
do { do {
PageDataOverflow page = index.getPageOverflow(next); PageDataOverflow page = index.getPageOverflow(next);
store.free(next, false); page.free();
next = page.getNextOverflow(); next = page.getNextOverflow();
} while (next != 0); } while (next != 0);
} }
...@@ -433,7 +439,6 @@ if (page == null) { ...@@ -433,7 +439,6 @@ if (page == null) {
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
write(); write();
index.getPageStore().checkUndo(getPos());
index.getPageStore().writePage(getPos(), data); index.getPageStore().writePage(getPos(), data);
data.truncate(index.getPageStore().getPageSize()); data.truncate(index.getPageStore().getPageSize());
} }
...@@ -516,7 +521,7 @@ if (page == null) { ...@@ -516,7 +521,7 @@ if (page == null) {
p2.write(); p2.write();
p2.data.truncate(index.getPageStore().getPageSize()); p2.data.truncate(index.getPageStore().getPageSize());
store.update(p2); store.update(p2);
store.free(getPos(), true); store.free(getPos());
if (parentPageId == ROOT) { if (parentPageId == ROOT) {
index.setRootPageId(session, newPos); index.setRootPageId(session, newPos);
} else { } else {
......
...@@ -259,7 +259,7 @@ public class PageDataNode extends PageData { ...@@ -259,7 +259,7 @@ public class PageDataNode extends PageData {
return false; return false;
} }
// this child is now empty // this child is now empty
index.getPageStore().free(page.getPos(), true); index.getPageStore().free(page.getPos());
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;
...@@ -269,12 +269,11 @@ public class PageDataNode extends PageData { ...@@ -269,12 +269,11 @@ public class PageDataNode extends PageData {
return false; return false;
} }
void freeChildren() throws SQLException { void freeRecursive() throws SQLException {
for (int i = 0; i <= entryCount; i++) { index.getPageStore().logUndo(this, data);
int childPageId = childPageIds[i]; index.getPageStore().free(getPos());
PageData child = index.getPage(childPageId, getPos()); for (int childPageId : childPageIds) {
index.getPageStore().free(childPageId, false); index.getPage(childPageId, getPos()).freeRecursive();
child.freeChildren();
} }
} }
...@@ -326,7 +325,6 @@ public class PageDataNode extends PageData { ...@@ -326,7 +325,6 @@ public class PageDataNode extends PageData {
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
write(); write();
index.getPageStore().checkUndo(getPos());
index.getPageStore().writePage(getPos(), data); index.getPageStore().writePage(getPos(), data);
} }
...@@ -409,7 +407,7 @@ public class PageDataNode extends PageData { ...@@ -409,7 +407,7 @@ public class PageDataNode extends PageData {
p.setParentPageId(newPos); p.setParentPageId(newPos);
store.update(p); store.update(p);
} }
store.free(getPos(), true); store.free(getPos());
} }
/** /**
......
...@@ -180,7 +180,6 @@ public class PageDataOverflow extends Page { ...@@ -180,7 +180,6 @@ public class PageDataOverflow extends Page {
public void write(DataPage buff) throws SQLException { public void write(DataPage buff) throws SQLException {
write(); write();
store.checkUndo(getPos());
store.writePage(getPos(), data); store.writePage(getPos(), data);
} }
...@@ -236,7 +235,7 @@ public class PageDataOverflow extends Page { ...@@ -236,7 +235,7 @@ public class PageDataOverflow extends Page {
p1.setOverflow(getPos(), newPos); p1.setOverflow(getPos(), newPos);
} }
store.update(p); store.update(p);
store.free(getPos(), true); store.free(getPos());
} }
private void setNext(int old, int nextPage) throws SQLException { private void setNext(int old, int nextPage) throws SQLException {
...@@ -248,4 +247,9 @@ public class PageDataOverflow extends Page { ...@@ -248,4 +247,9 @@ public class PageDataOverflow extends Page {
data.setInt(START_NEXT_OVERFLOW, nextPage); data.setInt(START_NEXT_OVERFLOW, nextPage);
} }
void free() throws SQLException {
store.logUndo(this, data);
store.free(getPos());
}
} }
...@@ -393,6 +393,7 @@ public class PageStore implements CacheWriter { ...@@ -393,6 +393,7 @@ public class PageStore implements CacheWriter {
break; break;
} }
} }
writeIndexRowCounts();
writeBack(); writeBack();
// truncate the log // truncate the log
recoveryRunning = true; recoveryRunning = true;
...@@ -867,13 +868,22 @@ public class PageStore implements CacheWriter { ...@@ -867,13 +868,22 @@ public class PageStore implements CacheWriter {
fileLength = newLength; fileLength = newLength;
} }
/**
* Add a page to the free list. The undo log entry must have been written.
*
* @param pageId the page id
*/
public void free(int pageId) throws SQLException {
free(pageId, true);
}
/** /**
* 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 undo if the undo record must have been written * @param undo if the undo record must have been written
*/ */
public void free(int pageId, boolean undo) throws SQLException { void free(int pageId, boolean undo) throws SQLException {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
// trace.debug("freePage " + pageId); // trace.debug("freePage " + pageId);
} }
...@@ -1036,9 +1046,9 @@ public class PageStore implements CacheWriter { ...@@ -1036,9 +1046,9 @@ public class PageStore implements CacheWriter {
openIndex.close(systemSession); openIndex.close(systemSession);
} }
allocatePage(PAGE_ID_META_ROOT); allocatePage(PAGE_ID_META_ROOT);
writeIndexRowCounts();
recoveryRunning = false; recoveryRunning = false;
reservedPages = null; reservedPages = null;
writeIndexRowCounts();
writeBack(); writeBack();
// clear the cache because it contains pages with closed indexes // clear the cache because it contains pages with closed indexes
cache.clear(); cache.clear();
...@@ -1548,16 +1558,4 @@ public class PageStore implements CacheWriter { ...@@ -1548,16 +1558,4 @@ public class PageStore implements CacheWriter {
return true; return true;
} }
/**
* Check if the undo entry for the given page has been written.
*
* @param pageId the page id
*/
public void checkUndo(int pageId) throws SQLException {
if (SysProperties.CHECK && !recoveryRunning) {
// ensure the undo entry is already written
// log.addUndo(pageId, null);
}
}
} }
...@@ -36,6 +36,7 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -36,6 +36,7 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
} }
public void test() throws Exception { public void test() throws Exception {
testDuplicateKey();
testUpdateOverflow(); testUpdateOverflow();
testTruncateReconnect(); testTruncateReconnect();
testReverseIndex(); testReverseIndex();
...@@ -56,6 +57,22 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -56,6 +57,22 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
testFuzzOperations(); testFuzzOperations();
} }
private void testDuplicateKey() throws SQLException {
deleteDb("pageStore");
Connection conn;
conn = getConnection("pageStore;PAGE_STORE=TRUE");
Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)");
stat.execute("insert into test values(0, space(3000))");
try {
stat.execute("insert into test values(0, space(3000))");
} catch (SQLException e) {
// ignore
}
stat.execute("select * from test");
conn.close();
}
private void testTruncateReconnect() throws SQLException { private void testTruncateReconnect() throws SQLException {
if (config.memory) { if (config.memory) {
return; return;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论