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

--no commit message

--no commit message
上级 87e8a128
......@@ -35,6 +35,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / 2007-TODO</h3><ul>
<li>In some situations the log file got corrupt if the process was terminated while the database was opening.
<li>Using ;RECOVER=1 in the database URL threw a syntax exception. Fixed.
<li>If a CLOB or BLOB was deleted in a transaction and the database crashed before the transaction was committed or rolled back,
the object was lost if it was large. Fixed.
<li>Now using ant-build.properties. The jdk is automatically updated when using ant codeswitch_...
......
......@@ -3364,6 +3364,9 @@ public class Parser {
} else if(readIf("DB_CLOSE_ON_EXIT")) {
read();
return new NoOperation(session);
} else if(readIf("RECOVER")) {
read();
return new NoOperation(session);
} else if(readIf("SCHEMA")) {
Set command = new Set(session, SetTypes.SCHEMA);
command.setString(readAliasIdentifier());
......
......@@ -523,7 +523,7 @@ public class Database implements DataHandler {
removeUnusedStorages();
systemSession.commit();
if(!readOnly) {
emergencyReserve = openFile(createTempFile(), true);
emergencyReserve = openFile(createTempFile(), false);
emergencyReserve.autoDelete();
emergencyReserve.setLength(Constants.EMERGENCY_SPACE_INITIAL);
}
......
......@@ -194,7 +194,9 @@ public class DiskFile implements CacheWriter {
ObjectArray list = database.getAllStorages();
for(int i=0; i<list.size(); i++) {
Storage s = (Storage)list.get(i);
database.removeStorage(s.getId(), this);
if(s.getDiskFile() == this) {
database.removeStorage(s.getId(), this);
}
}
reset();
initAlreadyTried = false;
......@@ -304,6 +306,7 @@ public class DiskFile implements CacheWriter {
}
synchronized void flush() throws SQLException {
database.checkPowerOff();
ObjectArray list = cache.getAllChanged();
CacheObject.sort(list);
for(int i=0; i<list.size(); i++) {
......
......@@ -281,15 +281,6 @@ public class LogFile {
default:
throw Message.getInternalError("type="+type);
}
if(undo) {
int posNow = getBlock();
in.setInt(0, -blocks);
in.fill(blocks * BLOCK_SIZE);
in.updateChecksum();
go(pos);
file.write(in.getBytes(), 0, BLOCK_SIZE * blocks);
go(posNow);
}
return true;
}
......@@ -341,7 +332,7 @@ public class LogFile {
if(bufferPos > 0) {
if(file == null) {
throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
}
}
file.write(buffer, 0, bufferPos);
for(int i=0; i<unwritten.size(); i++) {
Record r = (Record) unwritten.get(i);
......
......@@ -41,7 +41,7 @@ public class LogSystem {
public LogSystem(Database database, String fileNamePrefix, boolean readOnly) throws SQLException {
this.database = database;
this.readOnly = readOnly;
if(database==null || readOnly) {
if (database == null || readOnly) {
return;
}
this.fileNamePrefix = fileNamePrefix;
......@@ -64,43 +64,43 @@ public class LogSystem {
private void flushAndCloseUnused() throws SQLException {
currentLog.flush();
DiskFile file = database.getDataFile();
if(file == null) {
if (file == null) {
return;
}
file.flush();
if(containsInDoubtTransactions()) {
if (containsInDoubtTransactions()) {
// if there are any in-doubt transactions (even if they are resolved), can't update or delete the log files
return;
}
Session[] sessions = database.getSessions();
int firstUncommittedLog = currentLog.getId();
int firstUncommittedPos = currentLog.getPos();
for(int i=0; i<sessions.length; i++) {
for (int i = 0; i < sessions.length; i++) {
Session session = sessions[i];
int log = session.getFirstUncommittedLog();
int pos = session.getFirstUncommittedPos();
if(pos != LOG_WRITTEN) {
if(log < firstUncommittedLog || (log==firstUncommittedLog && pos < firstUncommittedPos)) {
if (pos != LOG_WRITTEN) {
if (log < firstUncommittedLog || (log == firstUncommittedLog && pos < firstUncommittedPos)) {
firstUncommittedLog = log;
firstUncommittedPos = pos;
}
}
}
for(int i = activeLogs.size()-1; i>=0; i--) {
for (int i = activeLogs.size() - 1; i >= 0; i--) {
LogFile l = (LogFile) activeLogs.get(i);
if(l.getId() < firstUncommittedLog) {
if (l.getId() < firstUncommittedLog) {
l.setFirstUncommittedPos(LOG_WRITTEN);
} else if(l.getId() == firstUncommittedLog) {
if(firstUncommittedPos == l.getPos()) {
} else if (l.getId() == firstUncommittedLog) {
if (firstUncommittedPos == l.getPos()) {
l.setFirstUncommittedPos(LOG_WRITTEN);
} else {
l.setFirstUncommittedPos(firstUncommittedPos);
}
}
}
for(int i = 0; i < activeLogs.size(); i++) {
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
if(l.getFirstUncommittedPos() == LOG_WRITTEN) {
if (l.getFirstUncommittedPos() == LOG_WRITTEN) {
l.close(deleteOldLogFilesAutomatically);
activeLogs.remove(i);
i--;
......@@ -109,45 +109,45 @@ public class LogSystem {
}
public void close() throws SQLException {
if(database==null || readOnly) {
if (database == null || readOnly) {
return;
}
synchronized(database) {
synchronized (database) {
// TODO refactor flushing and closing files when we know what to do exactly
SQLException closeException = null;
try {
flushAndCloseUnused();
if(!containsInDoubtTransactions()) {
if (!containsInDoubtTransactions()) {
checkpoint();
}
} catch (SQLException e) {
closeException = e;
}
for(int i=0; i<activeLogs.size(); i++) {
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
try {
// if there are any in-doubt transactions (even if they are resolved), can't delete the log files
if(l.getFirstUncommittedPos() == LOG_WRITTEN && !containsInDoubtTransactions()) {
if (l.getFirstUncommittedPos() == LOG_WRITTEN && !containsInDoubtTransactions()) {
l.close(deleteOldLogFilesAutomatically);
} else {
l.close(false);
}
} catch(SQLException e) {
} catch (SQLException e) {
// TODO log exception
if(closeException == null) {
if (closeException == null) {
closeException = e;
}
}
}
database = null;
if(closeException != null) {
if (closeException != null) {
throw closeException;
}
}
}
boolean needMoreUndo() {
return sessions.size()>0;
return sessions.size() > 0;
}
void addUndoLogRecord(LogFile log, int logRecordId, int sessionId) {
......@@ -156,12 +156,12 @@ public class LogSystem {
}
public boolean recover() throws SQLException {
if(database==null) {
if (database == null) {
return false;
}
synchronized(database) {
synchronized (database) {
undo = new ObjectArray();
for(int i=0; i<activeLogs.size(); i++) {
for (int i = 0; i < activeLogs.size(); i++) {
LogFile log = (LogFile) activeLogs.get(i);
log.redoAllGoEnd();
}
......@@ -170,16 +170,16 @@ public class LogSystem {
int end = currentLog.getPos();
Object[] states = sessions.values().toArray();
inDoubtTransactions = new ObjectArray();
for(int i=0; i<states.length; i++) {
SessionState state = (SessionState)states[i];
if(state.inDoubtTransaction != null) {
for (int i = 0; i < states.length; i++) {
SessionState state = (SessionState) states[i];
if (state.inDoubtTransaction != null) {
inDoubtTransactions.add(state.inDoubtTransaction);
}
}
for(int i=undo.size()-1; i>=0 && sessions.size()>0; i--) {
database.setProgress(DatabaseEventListener.STATE_RECOVER, null, undo.size()-1-i, undo.size());
LogRecord record = (LogRecord)undo.get(i);
if(sessions.get(new Integer(record.sessionId)) != null) {
for (int i = undo.size() - 1; i >= 0 && sessions.size() > 0; i--) {
database.setProgress(DatabaseEventListener.STATE_RECOVER, null, undo.size() - 1 - i, undo.size());
LogRecord record = (LogRecord) undo.get(i);
if (sessions.get(new Integer(record.sessionId)) != null) {
// undo only if the session is not yet committed
record.log.undo(record.logRecordId);
database.getDataFile().flushRedoLog();
......@@ -190,6 +190,9 @@ public class LogSystem {
boolean fileChanged = undo.size() > 0;
undo = null;
storages.clear();
if (fileChanged && !containsInDoubtTransactions()) {
checkpoint();
}
return fileChanged;
}
}
......@@ -198,11 +201,11 @@ public class LogSystem {
String path = FileUtils.getParent(fileNamePrefix);
String[] list = FileUtils.listFiles(path);
activeLogs = new ObjectArray();
for(int i=0; i<list.length; i++) {
for (int i = 0; i < list.length; i++) {
String s = list[i];
LogFile l = LogFile.openIfLogFile(this, fileNamePrefix, s);
if(l != null) {
if(l.getPos() == LOG_WRITTEN) {
if (l != null) {
if (l.getPos() == LOG_WRITTEN) {
l.close(deleteOldLogFilesAutomatically);
} else {
activeLogs.add(l);
......@@ -211,19 +214,19 @@ public class LogSystem {
}
activeLogs.sort(new Comparator() {
public int compare(Object a, Object b) {
return ((LogFile)a).getId() - ((LogFile)b).getId();
return ((LogFile) a).getId() - ((LogFile) b).getId();
}
});
if(activeLogs.size()==0) {
if (activeLogs.size() == 0) {
LogFile l = new LogFile(this, 0, fileNamePrefix);
activeLogs.add(l);
}
currentLog = (LogFile) activeLogs.get(activeLogs.size()-1);
currentLog = (LogFile) activeLogs.get(activeLogs.size() - 1);
}
Storage getStorageForRecovery(int id) throws SQLException {
boolean dataFile;
if(id < 0) {
if (id < 0) {
dataFile = false;
id = -id;
} else {
......@@ -231,7 +234,7 @@ public class LogSystem {
}
Integer i = new Integer(id);
Storage storage = (Storage) storages.get(i);
if(storage == null) {
if (storage == null) {
storage = database.getStorage(null, id, dataFile);
storages.put(i, storage);
}
......@@ -240,8 +243,8 @@ public class LogSystem {
boolean isSessionCommitted(int sessionId, int logId, int pos) {
Integer key = new Integer(sessionId);
SessionState state = (SessionState)sessions.get(key);
if(state == null) {
SessionState state = (SessionState) sessions.get(key);
if (state == null) {
return true;
}
return state.isCommitted(logId, pos);
......@@ -249,8 +252,8 @@ public class LogSystem {
void setLastCommitForSession(int sessionId, int logId, int pos) {
Integer key = new Integer(sessionId);
SessionState state = (SessionState)sessions.get(key);
if(state == null) {
SessionState state = (SessionState) sessions.get(key);
if (state == null) {
state = new SessionState();
sessions.put(key, state);
state.sessionId = sessionId;
......@@ -262,8 +265,8 @@ public class LogSystem {
void setPreparedCommitForSession(LogFile log, int sessionId, int pos, String transaction, int blocks) {
Integer key = new Integer(sessionId);
SessionState state = (SessionState)sessions.get(key);
if(state == null) {
SessionState state = (SessionState) sessions.get(key);
if (state == null) {
state = new SessionState();
sessions.put(key, state);
state.sessionId = sessionId;
......@@ -282,57 +285,57 @@ public class LogSystem {
}
public void prepareCommit(Session session, String transaction) throws SQLException {
if(database==null || readOnly) {
if (database == null || readOnly) {
return;
}
synchronized(database) {
synchronized (database) {
currentLog.prepareCommit(session, transaction);
}
}
public void commit(Session session) throws SQLException {
if(database==null || readOnly) {
if (database == null || readOnly) {
return;
}
synchronized(database) {
synchronized (database) {
currentLog.commit(session);
session.setAllCommitted();
}
}
public void flush() throws SQLException {
if(database==null || readOnly) {
if (database == null || readOnly) {
return;
}
synchronized(database) {
synchronized (database) {
currentLog.flush();
}
}
public void addTruncate(Session session, DiskFile file, int storageId, int recordId, int blockCount) throws SQLException {
if(database==null || disabled) {
if (database == null || disabled) {
return;
}
synchronized(database) {
synchronized (database) {
database.checkWritingAllowed();
if(!file.isDataFile()) {
if (!file.isDataFile()) {
storageId = -storageId;
}
currentLog.addTruncate(session, storageId, recordId, blockCount);
if(currentLog.getFileSize() > maxLogSize) {
if (currentLog.getFileSize() > maxLogSize) {
checkpoint();
}
}
}
public void add(Session session, DiskFile file, Record record) throws SQLException {
if(database==null || disabled) {
if (database == null || disabled) {
return;
}
synchronized(database) {
synchronized (database) {
database.checkWritingAllowed();
int storageId = record.getStorageId();
if(!file.isDataFile()) {
if (!file.isDataFile()) {
storageId = -storageId;
}
int log = currentLog.getId();
......@@ -340,39 +343,40 @@ public class LogSystem {
session.addLogPos(log, pos);
record.setLastLog(log, pos);
currentLog.add(session, storageId, record);
if(currentLog.getFileSize() > maxLogSize) {
if (currentLog.getFileSize() > maxLogSize) {
checkpoint();
}
}
}
public void checkpoint() throws SQLException {
if(database==null || readOnly || disabled) {
if (database == null || readOnly || disabled) {
return;
}
synchronized(database) {
synchronized (database) {
flushAndCloseUnused();
currentLog = new LogFile(this, currentLog.getId()+1, fileNamePrefix);
currentLog = new LogFile(this, currentLog.getId() + 1, fileNamePrefix);
activeLogs.add(currentLog);
writeSummary();
currentLog.flush();
}
}
private void writeSummary() throws SQLException {
if(database==null || readOnly || disabled) {
if (database == null || readOnly || disabled) {
return;
}
byte[] summary;
DiskFile file;
file = database.getDataFile();
summary = file.getSummary();
if(summary != null) {
if (summary != null) {
currentLog.addSummary(true, summary);
}
if(database.getLogIndexChanges() || database.getIndexSummaryValid()) {
if (database.getLogIndexChanges() || database.getIndexSummaryValid()) {
file = database.getIndexFile();
summary = file.getSummary();
if(summary != null) {
if (summary != null) {
currentLog.addSummary(false, summary);
}
} else {
......@@ -398,8 +402,8 @@ public class LogSystem {
}
public void sync() throws SQLException {
synchronized(database) {
if(currentLog != null) {
synchronized (database) {
if (currentLog != null) {
currentLog.flush();
currentLog.sync();
}
......
......@@ -34,6 +34,7 @@ import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.store.LogFile;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils;
......@@ -300,8 +301,9 @@ public class Recover implements DataHandler {
private void dumpLob(String fileName, boolean lobCompression) {
FileOutputStream out = null;
FileStore store = null;
int size = 0;
String n = fileName + (lobCompression ? ".comp" : "") + ".txt";
try {
String n = fileName + (lobCompression ? ".comp" : "") + ".txt";
out = new FileOutputStream(n);
textStorage = Database.isTextStorage(fileName, false);
byte[] magic = Database.getMagic(textStorage);
......@@ -315,13 +317,24 @@ public class Recover implements DataHandler {
break;
}
out.write(buffer, 0, l);
size += l;
}
} catch(Throwable e) {
logError(fileName, e);
// this is usually not a problem, because we try both compressed and uncompressed
if(log) {
logError(fileName, e);
}
} finally {
IOUtils.closeSilently(out);
closeSilently(store);
}
if(size == 0) {
try {
FileUtils.delete(n);
} catch (SQLException e) {
logError(n, e);
}
}
}
private void dumpLog(String fileName) throws SQLException {
......@@ -371,14 +384,17 @@ public class Recover implements DataHandler {
byte[] b2 = new byte[blocks * blockSize];
System.arraycopy(buff, 0, b2, 0, blockSize);
buff = b2;
store.readFully(buff, blockSize, blocks * blockSize - blockSize);
try {
store.readFully(buff, blockSize, blocks * blockSize - blockSize);
} catch(SQLException e) {
break;
}
s = DataPage.create(this, buff);
s.check(blocks * blockSize);
} else {
s.reset();
}
blocks = s.readInt();
if(blocks<=0) {
s.reset();
blocks = Math.abs(s.readInt());
if(blocks==0) {
writer.println("// [" + pos+"] blocks: "+blocks+" (end)");
break;
} else {
......@@ -386,12 +402,12 @@ public class Recover implements DataHandler {
int sessionId = s.readInt();
if(type == 'P') {
String transaction = s.readString();
writer.println("// prepared session:"+sessionId+" tx: " + transaction);
writer.println("// prepared session:"+sessionId+" tx:" + transaction);
} else if(type == 'C') {
writer.println("// commit session:" + sessionId);
} else {
int storageId = s.readInt();
int recordId = s.readInt();
int recId = s.readInt();
int blockCount = s.readInt();
if(type != 'T') {
s.readDataPageNoSize();
......@@ -404,21 +420,21 @@ public class Recover implements DataHandler {
if(sumLength > 0) {
s.read(summary, 0, sumLength);
}
writer.println("// summary session:"+sessionId+" fileType: " + fileType + " sumLength: " + sumLength);
writer.println("// summary session:"+sessionId+" fileType:" + fileType + " sumLength:" + sumLength);
dumpSummary(writer, summary);
break;
}
case 'T':
writer.println("// truncate session:"+sessionId+" storage: " + storageId + " recordId: " + recordId + " blockCount: "+blockCount);
writer.println("// truncate session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
break;
case 'I':
writer.println("// insert session:"+sessionId+" storage: " + storageId + " recordId: " + recordId + " blockCount: "+blockCount);
writer.println("// insert session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
break;
case 'D':
writer.println("// delete session:"+sessionId+" storage: " + storageId + " recordId: " + recordId + " blockCount: "+blockCount);
writer.println("// delete session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
break;
default:
writer.println("// type?:"+type+" session:"+sessionId+" storage: " + storageId + " recordId: " + recordId + " blockCount: "+blockCount);
writer.println("// type?:"+type+" session:"+sessionId+" storage:" + storageId + " pos:" + recId + " blockCount:"+blockCount);
break;
}
}
......@@ -441,8 +457,20 @@ public class Recover implements DataHandler {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(summary));
int b2 = in.readInt();
for(int i=0; i<b2 / 8; i++) {
in.read();
int x = in.read();
if((i % 8) == 0) {
writer.print("// ");
}
writer.print(" " + Long.toString(i * 8) + ":");
for(int j=0; j<8; j++) {
writer.print(((x & 1) == 1) ? "1" : "0");
x >>>= 1;
}
if((i % 8) == 7) {
writer.println("");
}
}
writer.println("//");
int len = in.readInt();
for(int i=0; i<len; i++) {
int storageId = in.readInt();
......@@ -542,7 +570,7 @@ public class Recover implements DataHandler {
PrintWriter writer = null;
FileStore store = null;
try {
databaseName = fileName.substring(fileName.length() - Constants.SUFFIX_DATA_FILE.length());
databaseName = fileName.substring(0, fileName.length() - Constants.SUFFIX_DATA_FILE.length());
writer = getWriter(fileName, ".sql");
ObjectArray schema = new ObjectArray();
HashSet objectIdSet = new HashSet();
......@@ -578,6 +606,7 @@ public class Recover implements DataHandler {
blockCount = 1;
continue;
}
writer.println("-- block " + block + " - " + (block + blockCount-1));
try {
s.checkCapacity(blockCount * blockSize);
} catch(OutOfMemoryError e) {
......@@ -643,11 +672,12 @@ public class Recover implements DataHandler {
sb.append("INSERT INTO O_" + storageId + " VALUES(");
for(valueId=0; valueId<recordLength; valueId++) {
try {
data[valueId] = s.readValue();
Value v = s.readValue();
data[valueId] = v;
if(valueId>0) {
sb.append(", ");
}
sb.append(data[valueId].getSQL());
sb.append(v.getSQL());
} catch(Exception e) {
writeDataError(writer, "exception " + e, s.getBytes(), blockCount);
continue;
......
......@@ -117,14 +117,18 @@ public class FileUtils {
if(oldFile.getName().equals(newFile.getName())) {
throw Message.getInternalError("rename file old=new");
}
if(oldFile.exists() && !newFile.exists()) {
for(int i=0; i<16; i++) {
boolean ok = oldFile.renameTo(newFile);
if(ok) {
return;
}
wait(i);
if(!oldFile.exists()) {
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
}
if(newFile.exists()) {
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
}
for(int i=0; i<16; i++) {
boolean ok = oldFile.renameTo(newFile);
if(ok) {
return;
}
wait(i);
}
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论