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

In some cases (specially when using a small cache size or a large database over…

In some cases (specially when using a small cache size or a large database over 1 GB) for some operations like ALTER TABLE the data was written before the transaction log entries, which could cause a corrupt database if the process was killed during the ALTER TABLE operation.
上级 36ddbb47
...@@ -234,9 +234,10 @@ abstract class PageData extends Page { ...@@ -234,9 +234,10 @@ abstract class PageData extends Page {
} }
public boolean canRemove() { public boolean canRemove() {
if (changeCount >= index.getPageStore().getChangeCount()) { int test;
return false; // if (changeCount >= index.getPageStore().getChangeCount()) {
} // return false;
// }
return true; return true;
} }
......
...@@ -158,6 +158,11 @@ public class PageLog { ...@@ -158,6 +158,11 @@ public class PageLog {
*/ */
private BitSet usedLogPages; private BitSet usedLogPages;
/**
* This flag is set while freeing up pages.
*/
private boolean freeing;
PageLog(PageStore store) { PageLog(PageStore store) {
this.store = store; this.store = store;
dataBuffer = store.createData(); dataBuffer = store.createData();
...@@ -196,9 +201,20 @@ public class PageLog { ...@@ -196,9 +201,20 @@ public class PageLog {
currentDataPage = pageOut.getCurrentDataPageId(); currentDataPage = pageOut.getCurrentDataPageId();
pageOut.freeReserved(); pageOut.freeReserved();
} }
try {
freeing = true;
int first = 0;
int loopDetect = 1024, loopCount = 0;
PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator(store, firstTrunkPage); PageStreamTrunk.Iterator it = new PageStreamTrunk.Iterator(store, firstTrunkPage);
while (firstTrunkPage != 0 && firstTrunkPage < store.getPageCount()) { while (firstTrunkPage != 0 && firstTrunkPage < store.getPageCount()) {
PageStreamTrunk t = it.next(); PageStreamTrunk t = it.next();
if (loopCount++ >= loopDetect) {
first = t.getPos();
loopCount = 0;
loopDetect *= 2;
} else if (first != 0 && t != null && first == t.getPos()) {
throw DbException.throwInternalError("endless loop at " + t);
}
if (t == null) { if (t == null) {
if (it.canDelete()) { if (it.canDelete()) {
store.free(firstTrunkPage, false); store.free(firstTrunkPage, false);
...@@ -208,6 +224,9 @@ public class PageLog { ...@@ -208,6 +224,9 @@ public class PageLog {
t.free(currentDataPage); t.free(currentDataPage);
firstTrunkPage = t.getNextTrunk(); firstTrunkPage = t.getNextTrunk();
} }
} finally {
freeing = false;
}
} }
/** /**
...@@ -452,7 +471,7 @@ public class PageLog { ...@@ -452,7 +471,7 @@ public class PageLog {
* @param page the old page data * @param page the old page data
*/ */
void addUndo(int pageId, Data page) { void addUndo(int pageId, Data page) {
if (undo.get(pageId)) { if (undo.get(pageId) || freeing) {
return; return;
} }
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
......
...@@ -168,6 +168,11 @@ public class CacheLRU implements Cache { ...@@ -168,6 +168,11 @@ public class CacheLRU implements Cache {
} }
} }
if (changed.size() > 0) { if (changed.size() > 0) {
int fixMaybeReplacesChangeCount;
if (!flushed) {
writer.flushLog();
}
Collections.sort(changed); Collections.sort(changed);
int max = maxMemory; int max = maxMemory;
try { try {
......
...@@ -109,7 +109,7 @@ public class TestCache extends TestBase implements CacheWriter { ...@@ -109,7 +109,7 @@ public class TestCache extends TestBase implements CacheWriter {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
c.put(new Obj(i)); c.put(new Obj(i));
} }
assertEquals("0 1 2 3 ", out); assertEquals("flush 0 flush 1 flush 2 flush 3 ", out);
} }
/** /**
......
...@@ -34,10 +34,12 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -34,10 +34,12 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
* @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.check2", "true");
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
public void test() throws Exception { public void test() throws Exception {
testWriteTransactionLogBeforeData();
testDefrag(); testDefrag();
testInsertReverse(); testInsertReverse();
testInsertDelete(); testInsertDelete();
...@@ -66,6 +68,36 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -66,6 +68,36 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
deleteDb("pageStore"); deleteDb("pageStore");
} }
private void testWriteTransactionLogBeforeData() throws SQLException {
deleteDb("pageStore");
String url = getURL("pageStore;CACHE_SIZE=16;WRITE_DELAY=1000000", true);
Connection conn;
Statement stat;
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
stat.execute("create table test(name varchar) as select space(100000)");
for (int i = 0; i < 100; i++) {
stat.execute("create table test" + i + "(id int) as select x from system_range(1, 1000)");
}
conn.close();
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
stat.execute("drop table test0");
stat.execute("select * from test");
stat.execute("shutdown immediately");
try {
conn.close();
} catch (Exception e) {
// ignore
}
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
for (int i = 1; i < 100; i++) {
stat.execute("select * from test" + i);
}
conn.close();
}
private void testDefrag() throws SQLException { private void testDefrag() throws SQLException {
deleteDb("pageStore"); deleteDb("pageStore");
Connection conn = getConnection("pageStore;LOG=0;UNDO_LOG=0;LOCK_MODE=0"); Connection conn = getConnection("pageStore;LOG=0;UNDO_LOG=0;LOCK_MODE=0");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论