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

Page store: bugfixes.

上级 671e444f
......@@ -100,11 +100,7 @@ class PageDataLeaf extends PageData {
if (entryCount > 1) {
return entryCount / 2;
}
int todoIncorrect;
if (find(row.getPos()) != 1) {
System.out.println("todo " + find(row.getPos()));
}
return 1; // find(row.getPos()) + 1;
return find(row.getPos());
}
int offset = last - rowLength;
int[] newOffsets = new int[entryCount + 1];
......@@ -188,7 +184,7 @@ class PageDataLeaf extends PageData {
written = false;
readAllRows();
entryCount--;
if (entryCount <= 0) {
if (entryCount < 0) {
Message.throwInternalError();
}
int[] newOffsets = new int[entryCount];
......
......@@ -40,21 +40,33 @@ public class PageFreeList extends Record {
/**
* Allocate a page from the free list.
*
* @param exclude the exclude list or null
* @param first the first page to look for
* @return the page, or -1 if all pages are used
*/
int allocate() throws SQLException {
int allocate(BitField exclude, int first) throws SQLException {
if (full) {
return -1;
}
// TODO cache last result
int free = used.nextClearBit(0);
if (free >= pageCount) {
full = true;
return -1;
int start = Math.max(0, first - getPos());
while (true) {
int free = used.nextClearBit(start);
if (free >= pageCount) {
full = true;
return -1;
}
if (exclude != null && exclude.get(free + getPos())) {
start = exclude.nextClearBit(free + getPos()) - getPos();
if (start >= pageCount) {
return -1;
}
} else {
used.set(free);
store.updateRecord(this, true, data);
return free + getPos();
}
}
used.set(free);
store.updateRecord(this, true, data);
return free + getPos();
}
/**
......
......@@ -11,6 +11,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import org.h2.message.Trace;
import org.h2.util.BitField;
/**
* An input stream that reads from a page store.
......@@ -110,10 +111,14 @@ public class PageInputStream extends InputStream {
/**
* Set all pages as 'allocated' in the page store.
*
* @return the bit set
*/
void allocateAllPages() throws SQLException {
BitField allocateAllPages() throws SQLException {
BitField pages = new BitField();
int trunkPage = trunkNext;
while (trunkPage != 0) {
pages.set(trunkPage);
store.allocatePage(trunkPage);
PageStreamTrunk t = new PageStreamTrunk(store, trunkPage);
t.read();
......@@ -122,10 +127,12 @@ public class PageInputStream extends InputStream {
if (n == -1) {
break;
}
pages.set(n);
store.allocatePage(n);
}
trunkPage = t.getNextTrunk();
}
return pages;
}
int getDataPage() {
......
......@@ -36,6 +36,8 @@ import org.h2.value.Value;
* <li>1-4: page id</li>
* <li>5-: data</li>
* </ul>
* The log file is split into sections, each section starts with a new log id.
* A checkpoint starts a new section.
*/
public class PageLog {
......@@ -125,12 +127,38 @@ public class PageLog {
private int firstTrunkPage;
private int firstDataPage;
private Data data;
private int logId, logPos;
private int logSectionId, logPos;
private int firstLogId;
/**
* If the bit is set, the given page was written to the current log section.
* The undo entry of these pages doesn't need to be written again.
*/
private BitField undo = new BitField();
private IntIntHashMap logIdPageMap = new IntIntHashMap();
/**
* The undo entry of those pages was written in any log section.
* These pages may not be used in the transaction log.
*/
private BitField undoAll = new BitField();
/**
* The map of section ids (key) and data page where the section starts (value).
*/
private IntIntHashMap logSectionPageMap = new IntIntHashMap();
/**
* The session state map.
* Only used during recovery.
*/
private HashMap<Integer, SessionState> sessionStates = New.hashMap();
/**
* The map of pages used by the transaction log.
* Only used during recovery.
*/
private BitField usedLogPages;
PageLog(PageStore store) {
this.store = store;
data = store.createData();
......@@ -146,7 +174,7 @@ public class PageLog {
void openForWriting(int firstTrunkPage) throws SQLException {
trace.debug("log openForWriting firstPage:" + firstTrunkPage);
this.firstTrunkPage = firstTrunkPage;
pageOut = new PageOutputStream(store, firstTrunkPage);
pageOut = new PageOutputStream(store, firstTrunkPage, undoAll);
pageOut.reserve(1);
store.setLogFirstPage(firstTrunkPage, pageOut.getCurrentDataPageId());
buffer = new ByteArrayOutputStream();
......@@ -199,7 +227,7 @@ public class PageLog {
}
if (stage == RECOVERY_STAGE_ALLOCATE) {
PageInputStream in = new PageInputStream(store, firstTrunkPage, firstDataPage);
in.allocateAllPages();
usedLogPages = in.allocateAllPages();
return;
}
pageIn = new PageInputStream(store, firstTrunkPage, firstDataPage);
......@@ -224,6 +252,7 @@ public class PageLog {
}
store.writePage(pageId, data);
undo.set(pageId);
undoAll.set(pageId);
}
}
} else if (x == ADD || x == REMOVE) {
......@@ -295,7 +324,9 @@ public class PageLog {
for (int i = 0; i < count; i++) {
int pageId = in.readInt();
if (stage == RECOVERY_STAGE_REDO) {
store.freePage(pageId, false, null);
if (!usedLogPages.get(pageId)) {
store.freePage(pageId, false, null);
}
}
}
} else {
......@@ -311,6 +342,9 @@ public class PageLog {
throw Message.convertIOException(e, "recover");
}
undo = new BitField();
if (stage == RECOVERY_STAGE_REDO) {
usedLogPages = null;
}
}
/**
......@@ -372,6 +406,7 @@ public class PageLog {
trace.debug("log undo " + pageId);
}
undo.set(pageId);
undoAll.set(pageId);
out.write(UNDO);
out.writeInt(pageId);
out.write(page.getBytes(), 0, store.getPageSize());
......@@ -482,8 +517,8 @@ public class PageLog {
trace.debug("log " + (add?"+":"-") + " s:" + session.getId() + " table:" + tableId +
" row:" + row);
}
session.addLogPos(logId, logPos);
row.setLastLog(logId, logPos);
session.addLogPos(logSectionId, logPos);
row.setLastLog(logSectionId, logPos);
data.reset();
data.checkCapacity(row.getByteCount(data));
......@@ -499,7 +534,7 @@ public class PageLog {
throw Message.convertIOException(e, null);
}
}
/**
* A table is truncated.
*
......@@ -511,7 +546,7 @@ public class PageLog {
if (trace.isDebugEnabled()) {
trace.debug("log truncate s:" + session.getId() + " table:" + tableId);
}
session.addLogPos(logId, logPos);
session.addLogPos(logSectionId, logPos);
data.reset();
out.write(TRUNCATE);
out.writeInt(session.getId());
......@@ -545,14 +580,14 @@ public class PageLog {
throw Message.convertIOException(e, null);
}
undo = new BitField();
logId++;
logSectionId++;
pageOut.fillPage();
int currentDataPage = pageOut.getCurrentDataPageId();
logIdPageMap.put(logId, currentDataPage);
logSectionPageMap.put(logSectionId, currentDataPage);
}
int getLogId() {
return logId;
int getLogSectionId() {
return logSectionId;
}
/**
......@@ -564,13 +599,13 @@ public class PageLog {
if (firstUncommittedLog == 0) {
return;
}
int firstDataPageToKeep = logIdPageMap.get(firstUncommittedLog);
int firstDataPageToKeep = logSectionPageMap.get(firstUncommittedLog);
firstTrunkPage = removeUntil(firstTrunkPage, firstDataPageToKeep);
store.setLogFirstPage(firstTrunkPage, firstDataPageToKeep);
while (firstLogId < firstUncommittedLog) {
if (firstLogId > 0) {
// there is no entry for log 0
logIdPageMap.remove(firstLogId);
logSectionPageMap.remove(firstLogId);
}
firstLogId++;
}
......
......@@ -11,6 +11,7 @@ import java.io.OutputStream;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.util.BitField;
import org.h2.util.IntArray;
/**
......@@ -21,6 +22,8 @@ public class PageOutputStream extends OutputStream {
private PageStore store;
private final Trace trace;
private int trunkPageId;
private final BitField exclude;
private int trunkNext;
private IntArray reservedPages = new IntArray();
private PageStreamTrunk trunk;
......@@ -38,10 +41,11 @@ public class PageOutputStream extends OutputStream {
* @param store the page store
* @param trunkPage the first trunk page (already allocated)
*/
public PageOutputStream(PageStore store, int trunkPage) {
public PageOutputStream(PageStore store, int trunkPage, BitField exclude) {
this.trace = store.getTrace();
this.store = store;
this.trunkPageId = trunkPage;
this.exclude = exclude;
}
/**
......@@ -64,10 +68,7 @@ public class PageOutputStream extends OutputStream {
}
// allocate the next trunk page as well
pagesToAllocate++;
for (int i = 0; i < pagesToAllocate; i++) {
int page = store.allocatePage();
reservedPages.add(page);
}
store.allocatePages(reservedPages, pagesToAllocate, exclude);
reserved += totalCapacity;
if (data == null) {
initNextData();
......
......@@ -31,11 +31,13 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.util.BitField;
import org.h2.util.Cache;
import org.h2.util.CacheLRU;
import org.h2.util.CacheObject;
import org.h2.util.CacheWriter;
import org.h2.util.FileUtils;
import org.h2.util.IntArray;
import org.h2.util.New;
import org.h2.util.ObjectArray;
import org.h2.util.StatementBuilder;
......@@ -69,11 +71,6 @@ import org.h2.value.ValueString;
*/
public class PageStore implements CacheWriter {
// TODO what if the log contains undo page for a later log page
// TODO what if the log contains a head page for a later log page
// TODO allocate log: must not use page if undo is in an active log
// TODO or don't redo if page is now a log page
// TODO var int: see google protocol buffers
// TODO don't save parent (only root); remove setPageId
// TODO implement checksum - 0 for empty
......@@ -354,7 +351,7 @@ public class PageStore implements CacheWriter {
private void switchLog() throws SQLException {
trace.debug("switchLog");
Session[] sessions = database.getSessions(true);
int firstUncommittedLog = log.getLogId();
int firstUncommittedLog = log.getLogSectionId();
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
int log = session.getFirstUncommittedLog();
......@@ -582,18 +579,38 @@ public class PageStore implements CacheWriter {
return getFreeListForPage(pageId).isUsed(pageId);
}
/**
* Allocate a number of pages.
*
* @param list the list where to add the allocated pages
* @param pagesToAllocate the number of pages to allocate
* @param exclude the exclude list
*/
void allocatePages(IntArray list, int pagesToAllocate, BitField exclude) throws SQLException {
int first = 0;
for (int i = 0; i < pagesToAllocate; i++) {
int page = allocatePage(exclude, first);
first = page;
list.add(page);
}
}
/**
* Allocate a page.
*
* @return the page id
*/
public int allocatePage() throws SQLException {
return allocatePage(null, 0);
}
private int allocatePage(BitField exclude, int first) throws SQLException {
int pos;
synchronized (database) {
// TODO could remember the first possible free list page
for (int i = 0;; i++) {
PageFreeList list = getFreeList(i);
pos = list.allocate();
pos = list.allocate(exclude, first);
if (pos >= 0) {
break;
}
......@@ -885,7 +902,7 @@ public class PageStore implements CacheWriter {
table.removeRow(systemSession, row);
}
}
/**
* Redo a truncate.
*
......@@ -929,7 +946,11 @@ public class PageStore implements CacheWriter {
int headPos = index.getHeadPos();
index.getTable().removeIndex(index);
if (index instanceof PageBtreeIndex) {
index.getSchema().remove(index);
if (index.isTemporary()) {
systemSession.removeLocalTempTableIndex(index);
} else {
index.getSchema().remove(index);
}
}
index.remove(systemSession);
if (reservedPages != null && reservedPages.containsKey(headPos)) {
......
......@@ -904,6 +904,13 @@ public class Recover extends Tool implements DataHandler {
// nothing to do
} else if (x == PageLog.CHECKPOINT) {
writer.println("-- checkpoint");
} else if (x == PageLog.FREE_LOG) {
int size = in.readInt();
StringBuilder buff = new StringBuilder("-- free");
for (int i = 0; i < size; i++) {
buff.append(' ').append(in.readInt());
}
writer.println(buff);
} else {
writer.println("-- end " + x);
break;
......
......@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.jdbc.JdbcConnection;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论