Closes #77, maintain database backups

The Scholar database is backed up on browser close. On startup, if the main database is damaged, the extension saves a copy of the damaged file and tries to restore from the last automatic backup. If it fails, it creates a new database file.

New methods:

Scholar.getScholarDatabase(string [ext])
Scholar.backupDatabase()
Scholar.moveToUnique(file, newFile) -- find a unique filename using the leafName of newFile as the suggested name (using the built-in Mozilla functionality) and move the file there
Scholar.Date.getFileDateString(file)
Scholar.Date.getFileTimeString(file)
This commit is contained in:
Dan Stillman 2006-08-01 23:10:31 +00:00
parent 9a0457b43e
commit d3bc693dab
3 changed files with 163 additions and 8 deletions

View file

@ -7,7 +7,6 @@ Scholar.DB = new function(){
var _transactionRollback;
var _transactionNestingLevel = 0;
// Privileged methods
this.query = query;
this.valueQuery = valueQuery;
this.rowQuery = rowQuery;
@ -359,13 +358,80 @@ Scholar.DB = new function(){
var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService);
// Get the storage directory
var file = Scholar.getScholarDirectory();
var file = Scholar.getScholarDatabase();
var backupFile = Scholar.getScholarDatabase('bak');
// This makes file point to PROFILE_DIR/<scholar dir>/<scholar database file>
file.append(SCHOLAR_CONFIG['DB_FILE']);
_connection = store.openDatabase(file);
catchBlock: try {
_connection = store.openDatabase(file);
}
catch (e){
if (e.name=='NS_ERROR_FILE_CORRUPTED'){
Scholar.debug('Database file corrupted', 1);
// No backup file! Eek!
if (!backupFile.exists()){
Scholar.debug('No backup file exists', 1);
// Save damaged filed
Scholar.debug('Saving damaged DB file with .damaged extension', 1);
var damagedFile = Scholar.getScholarDatabase('damaged');
Scholar.moveToUnique(file, damagedFile);
// Create new main database
var file = Scholar.getScholarDatabase();
_connection = store.openDatabase(file);
alert(Scholar.getString('db.dbCorruptedNoBackup'));
break catchBlock;
}
// Save damaged file
Scholar.debug('Saving damaged DB file with .damaged extension', 1);
var damagedFile = Scholar.getScholarDatabase('damaged');
Scholar.moveToUnique(file, damagedFile);
// Test the backup file
try {
_connection = store.openDatabase(backupFile);
}
// Can't open backup either
catch (e){
// Create new main database
var file = Scholar.getScholarDatabase();
_connection = store.openDatabase(file);
alert(Scholar.getString('db.dbRestoreFailed'));
break catchBlock;
}
_connection = undefined;
// Copy backup file to main DB file
Scholar.debug('Restoring main database from backup file', 1);
try {
backupFile.copyTo(backupFile.parent, SCHOLAR_CONFIG['DB_FILE']);
}
catch (e){
// TODO: deal with low disk space
throw (e);
}
// Open restored database
var file = Scholar.getScholarDirectory();
file.append(SCHOLAR_CONFIG['DB_FILE']);
_connection = store.openDatabase(file);
Scholar.debug('Database restored', 1);
var msg = Scholar.getString('db.dbRestored');
msg = msg.replace('%1', Scholar.Date.getFileDateString(backupFile))
msg = msg.replace('%2', Scholar.Date.getFileTimeString(backupFile))
alert(msg);
break catchBlock;
}
// Some other error that we don't yet know how to deal with
throw (e);
}
return _connection;
}

View file

@ -23,6 +23,8 @@ var Scholar = new function(){
this.getProfileDirectory = getProfileDirectory;
this.getScholarDirectory = getScholarDirectory;
this.getStorageDirectory = getStorageDirectory;
this.getScholarDatabase = getScholarDatabase;
this.backupDatabase = backupDatabase;
this.debug = debug;
this.varDump = varDump;
this.getString = getString;
@ -32,6 +34,7 @@ var Scholar = new function(){
this.arraySearch = arraySearch;
this.randomString = randomString;
this.getRandomID = getRandomID;
this.moveToUnique = moveToUnique;
// Public properties
this.version;
@ -93,6 +96,10 @@ var Scholar = new function(){
}
_shutdown = true;
Scholar.backupDatabase();
return true;
}
@ -126,6 +133,57 @@ var Scholar = new function(){
return file;
}
function getScholarDatabase(ext){
ext = ext ? '.' + ext : '';
var file = Scholar.getScholarDirectory();
file.append(SCHOLAR_CONFIG['DB_FILE'] + ext);
return file;
}
/*
* Back up the main database file
*
* This could probably create a corrupt file fairly easily if all changes
* haven't been flushed to disk -- proceed with caution
*/
function backupDatabase(){
if (Scholar.DB.transactionInProgress()){
Scholar.debug('Transaction in progress--skipping DB backup', 2);
return false;
}
Scholar.debug('Backing up database');
var file = Scholar.getScholarDatabase();
var backupFile = Scholar.getScholarDatabase('bak');
// Copy via a temporary file so we don't run into disk space issues
// after deleting the old backup file
var tmpFile = Scholar.getScholarDatabase('tmp');
if (tmpFile.exists()){
tmpFile.remove(null);
}
try {
file.copyTo(file.parent, tmpFile.leafName);
}
catch (e){
// TODO: deal with low disk space
throw (e);
}
// Remove old backup file
if (backupFile.exists()){
backupFile.remove(null);
}
tmpFile.moveTo(tmpFile.parent, backupFile.leafName);
return true;
}
/*
* Debug logging function
@ -342,6 +400,17 @@ var Scholar = new function(){
return rnd;
}
function moveToUnique(file, newFile){
newFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
var newName = newFile.leafName;
newFile.remove(null);
// Move file to unique name
file.moveTo(newFile.parent, newName);
return file;
}
};
@ -519,6 +588,8 @@ Scholar.Hash.prototype.has = function(in_key){
Scholar.Date = new function(){
this.sqlToDate = sqlToDate;
this.getFileDateString = getFileDateString;
this.getFileTimeString = getFileTimeString;
/**
* Convert an SQL date in the form '2006-06-13 11:03:05' into a JS Date object
@ -543,6 +614,20 @@ Scholar.Date = new function(){
return false;
}
}
function getFileDateString(file){
var date = new Date();
date.setTime(file.lastModifiedTime);
return date.toLocaleDateString();
}
function getFileTimeString(file){
var date = new Date();
date.setTime(file.lastModifiedTime);
return date.toLocaleTimeString();
}
}
Scholar.Browser = new function() {

View file

@ -79,4 +79,8 @@ creatorTypes.translator = Translator
ingester.scraping = Saving Item...
ingester.scrapeComplete = Item Saved.
ingester.scrapeError = Could Not Save Item.
ingester.scrapeErrorDescription = An error occurred while saving this item. Please try again. If this error persists, contact the translator author.
ingester.scrapeErrorDescription = An error occurred while saving this item. Please try again. If this error persists, contact the translator author.
db.dbCorruptedNoBackup = The Scholar database appears to have become corrupted, and no automatic backup is available.\n\nA new database file has been created. The damaged file was saved in your Scholar directory.
db.dbRestored = The Scholar database appears to have become corrupted.\n\nYour data was restored from the last automatic backup made on %1 at %2. The damaged file was saved in your Scholar directory.
db.dbRestoreFailed = The Scholar database appears to have become corrupted, and an attempt to restore from the last automatic backup failed.\n\nA new database file has been created. The damaged file was saved in your Scholar directory.