001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.log;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.SysProperties;
011: import org.h2.engine.Constants;
012: import org.h2.engine.Database;
013: import org.h2.engine.Session;
014: import org.h2.message.Message;
015: import org.h2.store.DataPage;
016: import org.h2.store.FileStore;
017: import org.h2.util.ObjectArray;
018:
019: /**
020: * Each session keeps a undo log if rollback is required.
021: */
022: public class UndoLog {
023: private Session session;
024: private Database database;
025: // TODO undo log entry: a chain would probably be faster
026: // and use less memory than an array
027: private ObjectArray records = new ObjectArray();
028: private FileStore file;
029: private DataPage rowBuff;
030: private int memoryUndo;
031:
032: public UndoLog(Session session) {
033: this .session = session;
034: this .database = session.getDatabase();
035: }
036:
037: public int size() {
038: if (SysProperties.CHECK && memoryUndo > records.size()) {
039: throw Message.getInternalError();
040: }
041: return records.size();
042: }
043:
044: public void clear() {
045: records.clear();
046: memoryUndo = 0;
047: if (file != null) {
048: file.closeAndDeleteSilently();
049: file = null;
050: rowBuff = null;
051: }
052: }
053:
054: public UndoLogRecord getAndRemoveLast() throws SQLException {
055: int i = records.size() - 1;
056: UndoLogRecord entry = (UndoLogRecord) records.get(i);
057: if (entry.isStored()) {
058: int start = Math
059: .max(0, i - database.getMaxMemoryUndo() / 2);
060: UndoLogRecord first = null;
061: for (int j = start; j <= i; j++) {
062: UndoLogRecord e = (UndoLogRecord) records.get(j);
063: if (e.isStored()) {
064: e.load(rowBuff, file, session);
065: memoryUndo++;
066: if (first == null) {
067: first = e;
068: }
069: }
070: }
071: first.seek(file);
072: }
073: UndoLogRecord r = (UndoLogRecord) records.remove(i);
074: if (!r.isStored()) {
075: memoryUndo--;
076: }
077: return entry;
078: }
079:
080: public void add(UndoLogRecord entry) throws SQLException {
081: records.add(entry);
082: if (!entry.isStored()) {
083: memoryUndo++;
084: }
085: if (memoryUndo > database.getMaxMemoryUndo()
086: && database.isPersistent()) {
087: if (file == null) {
088: String fileName = database.createTempFile();
089: file = database.openFile(fileName, "rw", false);
090: file.autoDelete();
091: file.seek(FileStore.HEADER_LENGTH);
092: rowBuff = DataPage.create(database,
093: Constants.DEFAULT_DATA_PAGE_SIZE);
094: DataPage buff = rowBuff;
095: for (int i = 0; i < records.size(); i++) {
096: UndoLogRecord r = (UndoLogRecord) records.get(i);
097: saveIfPossible(r, buff);
098: }
099: } else {
100: saveIfPossible(entry, rowBuff);
101: }
102: }
103: }
104:
105: private void saveIfPossible(UndoLogRecord r, DataPage buff)
106: throws SQLException {
107: if (!r.isStored() && r.canStore()) {
108: r.save(buff, file);
109: memoryUndo--;
110: }
111: }
112:
113: }
|