diff --git a/src/calibre/db/__init__.py b/src/calibre/db/__init__.py index 826b7a99fd..cde01b5f01 100644 --- a/src/calibre/db/__init__.py +++ b/src/calibre/db/__init__.py @@ -64,4 +64,6 @@ Various things that require other things before they can be migrated: columns/categories/searches info into self.field_metadata. Finally, implement metadata dirtied functionality. + 2. Catching DatabaseException and sqlite.Error when creating new + libraries/switching/on calibre startup. ''' diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index acf935d329..657d150dbb 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -405,13 +405,12 @@ class ChooseLibraryAction(InterfaceAction): else: return - prefs['library_path'] = loc #from calibre.utils.mem import memory #import weakref #from PyQt4.Qt import QTimer #self.dbref = weakref.ref(self.gui.library_view.model().db) #self.before_mem = memory()/1024**2 - self.gui.library_moved(loc) + self.gui.library_moved(loc, allow_rebuild=True) #QTimer.singleShot(5000, self.debug_leak) def debug_leak(self): @@ -455,7 +454,8 @@ class ChooseLibraryAction(InterfaceAction): self.choose_dialog_library_renamed = getattr(c, 'library_renamed', False) def choose_library_callback(self, newloc, copy_structure=False): - self.gui.library_moved(newloc, copy_structure=copy_structure) + self.gui.library_moved(newloc, copy_structure=copy_structure, + allow_rebuild=True) if getattr(self, 'choose_dialog_library_renamed', False): self.stats.rename(self.pre_choose_dialog_location, prefs['library_path']) self.build_menus() diff --git a/src/calibre/gui2/dialogs/choose_library.py b/src/calibre/gui2/dialogs/choose_library.py index 4e1424f0fc..62d6c4c437 100644 --- a/src/calibre/gui2/dialogs/choose_library.py +++ b/src/calibre/gui2/dialogs/choose_library.py @@ -13,7 +13,6 @@ from calibre.gui2.dialogs.choose_library_ui import Ui_Dialog from calibre.gui2 import error_dialog, choose_dir from calibre.constants import filesystem_encoding, iswindows from calibre import isbytestring, patheq -from calibre.utils.config import prefs from calibre.gui2.wizard import move_library from calibre.library.database2 import LibraryDatabase2 @@ -77,7 +76,6 @@ class ChooseLibrary(QDialog, Ui_Dialog): def perform_action(self, ac, loc): if ac in ('new', 'existing'): - prefs['library_path'] = loc self.callback(loc, copy_structure=self.copy_structure.isChecked()) else: self.db.prefs.disable_setting = True diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py index 42bb06ae29..9466b664be 100644 --- a/src/calibre/gui2/dialogs/restore_library.py +++ b/src/calibre/gui2/dialogs/restore_library.py @@ -120,18 +120,18 @@ def restore_database(db, parent=None): _show_success_msg(r, parent=parent) return True -def repair_library_at(library_path): - d = DBRestore(None, library_path) +def repair_library_at(library_path, parent=None): + d = DBRestore(parent, library_path) d.exec_() if d.rejected: return False r = d.restorer if r.tb is not None: - error_dialog(None, _('Failed'), + error_dialog(parent, _('Failed'), _('Restoring database failed, click Show details to see details'), det_msg=r.tb, show=True) return False - _show_success_msg(r) + _show_success_msg(r, parent=parent) return True diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 928e6c63ab..c12a15829a 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -18,8 +18,8 @@ from PyQt4.Qt import (Qt, SIGNAL, QTimer, QHelpEvent, QAction, QMenu, QIcon, pyqtSignal, QUrl, QDialog, QSystemTrayIcon, QApplication) -from calibre import prints -from calibre.constants import __appname__, isosx +from calibre import prints, force_unicode +from calibre.constants import __appname__, isosx, filesystem_encoding from calibre.utils.config import prefs, dynamic from calibre.utils.ipc.server import Server from calibre.library.database2 import LibraryDatabase2 @@ -41,7 +41,7 @@ from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin from calibre.gui2.tag_browser.ui import TagBrowserMixin from calibre.gui2.keyboard import Manager - +from calibre.library.sqlite import sqlite, DatabaseException class Listener(Thread): # {{{ @@ -475,7 +475,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ def booklists(self): return self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db - def library_moved(self, newloc, copy_structure=False, call_close=True): + def library_moved(self, newloc, copy_structure=False, call_close=True, + allow_rebuild=False): if newloc is None: return default_prefs = None try: @@ -484,7 +485,26 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ default_prefs = olddb.prefs except: olddb = None - db = LibraryDatabase2(newloc, default_prefs=default_prefs) + try: + db = LibraryDatabase2(newloc, default_prefs=default_prefs) + except (DatabaseException, sqlite.Error): + if not allow_rebuild: raise + import traceback + repair = question_dialog(self, _('Corrupted database'), + _('The library database at %s appears to be corrupted. Do ' + 'you want calibre to try and rebuild it automatically? ' + 'The rebuild may not be completely successful.') + % force_unicode(newloc, filesystem_encoding), + det_msg=traceback.format_exc() + ) + if repair: + from calibre.gui2.dialogs.restore_library import repair_library_at + if repair_library_at(newloc, parent=self): + db = LibraryDatabase2(newloc, default_prefs=default_prefs) + else: + return + else: + return if self.content_server is not None: self.content_server.set_database(db) self.library_path = newloc