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:
parent
9a0457b43e
commit
d3bc693dab
3 changed files with 163 additions and 8 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
Loading…
Reference in a new issue