Allow using a tweak to switch db backends

This commit is contained in:
Kovid Goyal 2013-07-19 21:11:08 +05:30
parent 8d4d404b5d
commit d0b826c39a
11 changed files with 80 additions and 103 deletions

View File

@ -118,65 +118,26 @@ def get_data_as_dict(self, prefix=None, authors_as_string=False, ids=None):
return data return data
def get_db_loader():
from calibre.utils.config_base import tweaks
if tweaks.get('use_new_db', False):
from calibre.db.legacy import LibraryDatabase as cls
import apsw
errs = (apsw.Error,)
else:
from calibre.library.database2 import LibraryDatabase2 as cls
from calibre.library.sqlite import sqlite, DatabaseException
errs = (sqlite.Error, DatabaseException)
return cls, errs
''' '''
Rewrite of the calibre database backend.
Broad Objectives:
* Use the sqlite db only as a datastore. i.e. do not do
sorting/searching/concatenation or anything else in sqlite. Instead
mirror the sqlite tables in memory, create caches and lookup maps from
them and create a set_* API that updates the memory caches and the sqlite
correctly.
* Move from keeping a list of books in memory as a cache to a per table
cache. This allows much faster search and sort operations at the expense
of slightly slower lookup operations. That slowdown can be mitigated by
keeping lots of maps and updating them in the set_* API. Also
get_categories becomes blazingly fast.
* Separate the database layer from the cache layer more cleanly. Rather
than having the db layer refer to the cache layer and vice versa, the
cache layer will refer to the db layer only and the new API will be
defined on the cache layer.
* Get rid of index_is_id and other poor design decisions
* Minimize the API as much as possible and define it cleanly
* Do not change the on disk format of metadata.db at all (this is for
backwards compatibility)
* Get rid of the need for a separate db access thread by switching to apsw
to access sqlite, which is thread safe
* The new API will have methods to efficiently do bulk operations and will
use shared/exclusive/pending locks to serialize access to the in-mem data
structures. Use the same locking scheme as sqlite itself does.
How this will proceed:
1. Create the new API
2. Create a test suite for it
3. Write a replacement for LibraryDatabase2 that uses the new API
internally
4. Lots of testing of calibre with the new LibraryDatabase2
5. Gradually migrate code to use the (much faster) new api wherever possible (the new api
will be exposed via db.new_api)
I plan to work on this slowly, in parallel to normal calibre development
work.
Various things that require other things before they can be migrated: Various things that require other things before they can be migrated:
1. From initialize_dynamic(): Also add custom 1. From initialize_dynamic(): Also add custom
columns/categories/searches info into columns/categories/searches info into
self.field_metadata. self.field_metadata.
2. Catching DatabaseException and sqlite.Error when creating new 2. Port library/restore.py
libraries/switching/on calibre startup. 3. Replace the metadatabackup thread with the new implementation when using the new backend.
3. Port library/restore.py 4. Check that content server reloading on metadata,db change, metadata
4. Replace the metadatabackup thread with the new implementation when using the new backend.
5. grep the sources for TODO
6. Check that content server reloading on metadata,db change, metadata
backup, refresh gui on calibredb add and moving libraries all work (check backup, refresh gui on calibredb add and moving libraries all work (check
them on windows as well for file locking issues) them on windows as well for file locking issues)
''' '''

View File

@ -19,9 +19,12 @@ from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog, from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
question_dialog, info_dialog, open_local_file, choose_dir) question_dialog, info_dialog, open_local_file, choose_dir)
from calibre.library.database2 import LibraryDatabase2
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
def db_class():
from calibre.db import get_db_loader
return get_db_loader()[0]
class LibraryUsageStats(object): # {{{ class LibraryUsageStats(object): # {{{
def __init__(self): def __init__(self):
@ -139,7 +142,7 @@ class MovedDialog(QDialog): # {{{
def accept(self): def accept(self):
newloc = unicode(self.loc.text()) newloc = unicode(self.loc.text())
if not LibraryDatabase2.exists_at(newloc): if not db_class.exists_at(newloc):
error_dialog(self, _('No library found'), error_dialog(self, _('No library found'),
_('No existing calibre library found at %s')%newloc, _('No existing calibre library found at %s')%newloc,
show=True) show=True)
@ -313,6 +316,7 @@ class ChooseLibraryAction(InterfaceAction):
self.qaction.setEnabled(enabled) self.qaction.setEnabled(enabled)
def rename_requested(self, name, location): def rename_requested(self, name, location):
LibraryDatabase = db_class()
loc = location.replace('/', os.sep) loc = location.replace('/', os.sep)
base = os.path.dirname(loc) base = os.path.dirname(loc)
newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + name, newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + name,
@ -328,10 +332,10 @@ class ChooseLibraryAction(InterfaceAction):
_('The folder %s already exists. Delete it first.') % _('The folder %s already exists. Delete it first.') %
newloc, show=True) newloc, show=True)
if (iswindows and len(newloc) > if (iswindows and len(newloc) >
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
return error_dialog(self.gui, _('Too long'), return error_dialog(self.gui, _('Too long'),
_('Path to library too long. Must be less than' _('Path to library too long. Must be less than'
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, ' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
show=True) show=True)
if not os.path.exists(loc): if not os.path.exists(loc):
error_dialog(self.gui, _('Not found'), error_dialog(self.gui, _('Not found'),
@ -387,16 +391,17 @@ class ChooseLibraryAction(InterfaceAction):
'rate of approximately 1 book every three seconds.'), show=True) 'rate of approximately 1 book every three seconds.'), show=True)
def restore_database(self): def restore_database(self):
LibraryDatabase = db_class()
m = self.gui.library_view.model() m = self.gui.library_view.model()
db = m.db db = m.db
if (iswindows and len(db.library_path) > if (iswindows and len(db.library_path) >
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
return error_dialog(self.gui, _('Too long'), return error_dialog(self.gui, _('Too long'),
_('Path to library too long. Must be less than' _('Path to library too long. Must be less than'
' %d characters. Move your library to a location with' ' %d characters. Move your library to a location with'
' a shorter path using Windows Explorer, then point' ' a shorter path using Windows Explorer, then point'
' calibre to the new location and try again.')% ' calibre to the new location and try again.')%
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
show=True) show=True)
from calibre.gui2.dialogs.restore_library import restore_database from calibre.gui2.dialogs.restore_library import restore_database

View File

@ -55,8 +55,8 @@ class Worker(Thread): # {{{
notify=False, replace=replace) notify=False, replace=replace)
def doit(self): def doit(self):
from calibre.library.database2 import LibraryDatabase2 from calibre.db import get_db_loader
newdb = LibraryDatabase2(self.loc, is_second_db=True) newdb = get_db_loader()[0](self.loc, is_second_db=True)
with closing(newdb): with closing(newdb):
self._doit(newdb) self._doit(newdb)
newdb.break_cycles() newdb.break_cycles()

View File

@ -15,7 +15,6 @@ from calibre.constants import (filesystem_encoding, iswindows,
get_portable_base) get_portable_base)
from calibre import isbytestring, patheq, force_unicode from calibre import isbytestring, patheq, force_unicode
from calibre.gui2.wizard import move_library from calibre.gui2.wizard import move_library
from calibre.library.database2 import LibraryDatabase2
class ChooseLibrary(QDialog, Ui_Dialog): class ChooseLibrary(QDialog, Ui_Dialog):
@ -86,6 +85,8 @@ class ChooseLibrary(QDialog, Ui_Dialog):
show=True) show=True)
return False return False
if ac in ('new', 'move'): if ac in ('new', 'move'):
from calibre.db import get_db_loader
LibraryDatabase = get_db_loader()[0]
if not empty: if not empty:
error_dialog(self, _('Not empty'), error_dialog(self, _('Not empty'),
_('The folder %s is not empty. Please choose an empty' _('The folder %s is not empty. Please choose an empty'
@ -93,10 +94,10 @@ class ChooseLibrary(QDialog, Ui_Dialog):
show=True) show=True)
return False return False
if (iswindows and len(loc) > if (iswindows and len(loc) >
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT):
error_dialog(self, _('Too long'), error_dialog(self, _('Too long'),
_('Path to library too long. Must be less than' _('Path to library too long. Must be less than'
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, ' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT,
show=True) show=True)
return False return False

View File

@ -15,8 +15,6 @@ from calibre.gui2 import (ORG_NAME, APP_UID, initialize_file_icon_provider,
Application, choose_dir, error_dialog, question_dialog, gprefs) Application, choose_dir, error_dialog, question_dialog, gprefs)
from calibre.gui2.main_window import option_parser as _option_parser from calibre.gui2.main_window import option_parser as _option_parser
from calibre.utils.config import prefs, dynamic from calibre.utils.config import prefs, dynamic
from calibre.library.database2 import LibraryDatabase2
from calibre.library.sqlite import sqlite, DatabaseException
if iswindows: if iswindows:
winutil = plugins['winutil'][0] winutil = plugins['winutil'][0]
@ -51,7 +49,8 @@ path_to_ebook to the database.
def find_portable_library(): def find_portable_library():
base = get_portable_base() base = get_portable_base()
if base is None: return if base is None:
return
import glob import glob
candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob( candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob(
os.path.join(base, u'*%smetadata.db'%os.sep))] os.path.join(base, u'*%smetadata.db'%os.sep))]
@ -123,7 +122,7 @@ def get_default_library_path():
def get_library_path(parent=None): def get_library_path(parent=None):
library_path = prefs['library_path'] library_path = prefs['library_path']
if library_path is None: # Need to migrate to new database layout if library_path is None: # Need to migrate to new database layout
base = os.path.expanduser('~') base = os.path.expanduser('~')
if iswindows: if iswindows:
base = winutil.special_folder_path(winutil.CSIDL_PERSONAL) base = winutil.special_folder_path(winutil.CSIDL_PERSONAL)
@ -181,7 +180,7 @@ class GuiRunner(QObject):
main = Main(self.opts, gui_debug=self.gui_debug) main = Main(self.opts, gui_debug=self.gui_debug)
if self.splash_screen is not None: if self.splash_screen is not None:
self.splash_screen.showMessage(_('Initializing user interface...')) self.splash_screen.showMessage(_('Initializing user interface...'))
with gprefs: # Only write gui.json after initialization is complete with gprefs: # Only write gui.json after initialization is complete
main.initialize(self.library_path, db, self.listener, self.actions) main.initialize(self.library_path, db, self.listener, self.actions)
if self.splash_screen is not None: if self.splash_screen is not None:
self.splash_screen.finish(main) self.splash_screen.finish(main)
@ -224,7 +223,7 @@ class GuiRunner(QObject):
try: try:
self.library_path = candidate self.library_path = candidate
db = LibraryDatabase2(candidate) db = self.db_class(candidate)
except: except:
error_dialog(self.splash_screen, _('Bad database location'), error_dialog(self.splash_screen, _('Bad database location'),
_('Bad database location %r. calibre will now quit.' _('Bad database location %r. calibre will now quit.'
@ -235,10 +234,12 @@ class GuiRunner(QObject):
self.start_gui(db) self.start_gui(db)
def initialize_db(self): def initialize_db(self):
from calibre.db import get_db_loader
db = None db = None
self.db_class, errs = get_db_loader()
try: try:
db = LibraryDatabase2(self.library_path) db = self.db_class(self.library_path)
except (sqlite.Error, DatabaseException): except errs:
repair = question_dialog(self.splash_screen, _('Corrupted database'), repair = question_dialog(self.splash_screen, _('Corrupted database'),
_('The library database at %s appears to be corrupted. Do ' _('The library database at %s appears to be corrupted. Do '
'you want calibre to try and rebuild it automatically? ' 'you want calibre to try and rebuild it automatically? '
@ -249,7 +250,7 @@ class GuiRunner(QObject):
) )
if repair: if repair:
if repair_library(self.library_path): if repair_library(self.library_path):
db = LibraryDatabase2(self.library_path) db = self.db_class(self.library_path)
except: except:
error_dialog(self.splash_screen, _('Bad database location'), error_dialog(self.splash_screen, _('Bad database location'),
_('Bad database location %r. Will start with ' _('Bad database location %r. Will start with '
@ -383,7 +384,7 @@ def shutdown_other(rc=None):
rc = build_pipe(print_error=False) rc = build_pipe(print_error=False)
if rc.conn is None: if rc.conn is None:
prints(_('No running calibre found')) prints(_('No running calibre found'))
return # No running instance found return # No running instance found
from calibre.utils.lock import singleinstance from calibre.utils.lock import singleinstance
rc.conn.send('shutdown:') rc.conn.send('shutdown:')
prints(_('Shutdown command sent, waiting for shutdown...')) prints(_('Shutdown command sent, waiting for shutdown...'))
@ -441,7 +442,7 @@ def main(args=sys.argv):
otherinstance = False otherinstance = False
try: try:
listener = Listener(address=gui_socket_address()) listener = Listener(address=gui_socket_address())
except socket.error: # Good si is correct (on UNIX) except socket.error: # Good si is correct (on UNIX)
otherinstance = True otherinstance = True
else: else:
# On windows only singleinstance can be trusted # On windows only singleinstance can be trusted
@ -458,7 +459,8 @@ if __name__ == '__main__':
try: try:
sys.exit(main()) sys.exit(main())
except Exception as err: except Exception as err:
if not iswindows: raise if not iswindows:
raise
tb = traceback.format_exc() tb = traceback.format_exc()
from PyQt4.QtGui import QErrorMessage from PyQt4.QtGui import QErrorMessage
logfile = os.path.join(os.path.expanduser('~'), 'calibre.log') logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
@ -470,3 +472,4 @@ if __name__ == '__main__':
unicode(tb).replace('\n', '<br>'), unicode(tb).replace('\n', '<br>'),
log.replace('\n', '<br>'))) log.replace('\n', '<br>')))

View File

@ -22,7 +22,7 @@ from calibre import prints, force_unicode
from calibre.constants import __appname__, isosx, filesystem_encoding from calibre.constants import __appname__, isosx, filesystem_encoding
from calibre.utils.config import prefs, dynamic from calibre.utils.config import prefs, dynamic
from calibre.utils.ipc.server import Server from calibre.utils.ipc.server import Server
from calibre.library.database2 import LibraryDatabase2 from calibre.db import get_db_loader
from calibre.customize.ui import interface_actions, available_store_plugins from calibre.customize.ui import interface_actions, available_store_plugins
from calibre.gui2 import (error_dialog, GetMetadata, open_url, from calibre.gui2 import (error_dialog, GetMetadata, open_url,
gprefs, max_available_height, config, info_dialog, Dispatcher, gprefs, max_available_height, config, info_dialog, Dispatcher,
@ -42,7 +42,6 @@ from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin
from calibre.gui2.tag_browser.ui import TagBrowserMixin from calibre.gui2.tag_browser.ui import TagBrowserMixin
from calibre.gui2.keyboard import Manager from calibre.gui2.keyboard import Manager
from calibre.gui2.auto_add import AutoAdder from calibre.gui2.auto_add import AutoAdder
from calibre.library.sqlite import sqlite, DatabaseException
from calibre.gui2.proceed import ProceedQuestion from calibre.gui2.proceed import ProceedQuestion
from calibre.gui2.dialogs.message_box import JobError from calibre.gui2.dialogs.message_box import JobError
from calibre.gui2.job_indicator import Pointer from calibre.gui2.job_indicator import Pointer
@ -572,12 +571,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
default_prefs = olddb.prefs default_prefs = olddb.prefs
from calibre.utils.formatter_functions import unload_user_template_functions from calibre.utils.formatter_functions import unload_user_template_functions
unload_user_template_functions(olddb.library_id ) unload_user_template_functions(olddb.library_id)
except: except:
olddb = None olddb = None
db_class, errs = get_db_loader()
try: try:
db = LibraryDatabase2(newloc, default_prefs=default_prefs) db = db_class(newloc, default_prefs=default_prefs)
except (DatabaseException, sqlite.Error): except errs:
if not allow_rebuild: if not allow_rebuild:
raise raise
import traceback import traceback
@ -591,7 +591,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
if repair: if repair:
from calibre.gui2.dialogs.restore_library import repair_library_at from calibre.gui2.dialogs.restore_library import repair_library_at
if repair_library_at(newloc, parent=self): if repair_library_at(newloc, parent=self):
db = LibraryDatabase2(newloc, default_prefs=default_prefs) db = db_class(newloc, default_prefs=default_prefs)
else: else:
return return
else: else:

View File

@ -14,7 +14,6 @@ from contextlib import closing
from PyQt4.Qt import (QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel, from PyQt4.Qt import (QWizard, QWizardPage, QPixmap, Qt, QAbstractListModel,
QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer, pyqtSignal) QVariant, QItemSelectionModel, SIGNAL, QObject, QTimer, pyqtSignal)
from calibre import __appname__, patheq from calibre import __appname__, patheq
from calibre.library.database2 import LibraryDatabase2
from calibre.library.move import MoveLibrary from calibre.library.move import MoveLibrary
from calibre.constants import (filesystem_encoding, iswindows, plugins, from calibre.constants import (filesystem_encoding, iswindows, plugins,
isportable) isportable)
@ -34,6 +33,10 @@ from calibre.customize.ui import device_plugins
if iswindows: if iswindows:
winutil = plugins['winutil'][0] winutil = plugins['winutil'][0]
def db_class():
from calibre.db import get_db_loader
return get_db_loader()[0]
# Devices {{{ # Devices {{{
class Device(object): class Device(object):
@ -623,7 +626,7 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
if oldloc and os.access(os.path.join(oldloc, 'metadata.db'), os.R_OK): if oldloc and os.access(os.path.join(oldloc, 'metadata.db'), os.R_OK):
# Move old library to new location # Move old library to new location
try: try:
db = LibraryDatabase2(oldloc) db = db_class()(oldloc)
except: except:
return move_library(None, newloc, parent, return move_library(None, newloc, parent,
callback) callback)
@ -636,13 +639,13 @@ def move_library(oldloc, newloc, parent, callback_on_complete):
return return
else: else:
# Create new library at new location # Create new library at new location
db = LibraryDatabase2(newloc) db = db_class()(newloc)
callback(newloc) callback(newloc)
return return
# Try to load existing library at new location # Try to load existing library at new location
try: try:
LibraryDatabase2(newloc) db_class()(newloc)
except Exception as err: except Exception as err:
det = traceback.format_exc() det = traceback.format_exc()
error_dialog(parent, _('Invalid database'), error_dialog(parent, _('Invalid database'),
@ -729,7 +732,7 @@ class LibraryPage(QWizardPage, LibraryUI):
def is_library_dir_suitable(self, x): def is_library_dir_suitable(self, x):
try: try:
return LibraryDatabase2.exists_at(x) or not os.listdir(x) return db_class().exists_at(x) or not os.listdir(x)
except: except:
return False return False
@ -745,10 +748,10 @@ class LibraryPage(QWizardPage, LibraryUI):
_('Select location for books')) _('Select location for books'))
if x: if x:
if (iswindows and len(x) > if (iswindows and len(x) >
LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): db_class().WINDOWS_LIBRARY_PATH_LIMIT):
return error_dialog(self, _('Too long'), return error_dialog(self, _('Too long'),
_('Path to library too long. Must be less than' _('Path to library too long. Must be less than'
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, ' %d characters.')%(db_class().WINDOWS_LIBRARY_PATH_LIMIT),
show=True) show=True)
if not os.path.exists(x): if not os.path.exists(x):
try: try:

View File

@ -3,13 +3,13 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''' Code to manage ebook library''' ''' Code to manage ebook library'''
def db(path=None, read_only=False): def db(path=None, read_only=False):
from calibre.library.database2 import LibraryDatabase2 from calibre.db import get_db_loader
from calibre.utils.config import prefs from calibre.utils.config import prefs
return LibraryDatabase2(path if path else prefs['library_path'], return get_db_loader()[0](path if path else prefs['library_path'],
read_only=read_only) read_only=read_only)
def generate_test_db(library_path, # {{{ def generate_test_db(library_path, # {{{
num_of_records=20000, num_of_records=20000,
num_of_authors=6000, num_of_authors=6000,
num_of_tags=10000, num_of_tags=10000,
@ -76,3 +76,4 @@ def current_library_name():
if path: if path:
return posixpath.basename(path) return posixpath.basename(path)

View File

@ -15,15 +15,18 @@ from calibre import preferred_encoding, prints, isbytestring
from calibre.utils.config import OptionParser, prefs, tweaks from calibre.utils.config import OptionParser, prefs, tweaks
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.metadata.book.base import field_from_string from calibre.ebooks.metadata.book.base import field_from_string
from calibre.library.database2 import LibraryDatabase2
from calibre.ebooks.metadata.opf2 import OPFCreator, OPF from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
from calibre.utils.date import isoformat from calibre.utils.date import isoformat
from calibre.db import get_db_loader
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating', FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
'formats', 'isbn', 'uuid', 'pubdate', 'cover', 'last_modified', 'formats', 'isbn', 'uuid', 'pubdate', 'cover', 'last_modified',
'identifiers']) 'identifiers'])
def db_class():
return get_db_loader()[0]
do_notify = True do_notify = True
def send_message(msg=''): def send_message(msg=''):
global do_notify global do_notify
@ -62,7 +65,7 @@ def get_db(dbpath, options):
dbpath = os.path.abspath(dbpath) dbpath = os.path.abspath(dbpath)
if options.dont_notify_gui: if options.dont_notify_gui:
do_notify = False do_notify = False
return LibraryDatabase2(dbpath) return db_class()(dbpath)
def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator, def do_list(db, fields, afields, sort_by, ascending, search_text, line_width, separator,
prefix, limit, subtitle='Books in the calibre database'): prefix, limit, subtitle='Books in the calibre database'):
@ -1101,7 +1104,7 @@ def command_backup_metadata(args, dbpath):
dbpath = opts.library_path dbpath = opts.library_path
if isbytestring(dbpath): if isbytestring(dbpath):
dbpath = dbpath.decode(preferred_encoding) dbpath = dbpath.decode(preferred_encoding)
db = LibraryDatabase2(dbpath) db = db_class()(dbpath)
book_ids = None book_ids = None
if opts.all: if opts.all:
book_ids = db.all_ids() book_ids = db.all_ids()
@ -1183,7 +1186,7 @@ def command_check_library(args, dbpath):
for i in list: for i in list:
print ' %-40.40s - %-40.40s'%(i[0], i[1]) print ' %-40.40s - %-40.40s'%(i[0], i[1])
db = LibraryDatabase2(dbpath) db = db_class()(dbpath)
checker = CheckLibrary(dbpath, db) checker = CheckLibrary(dbpath, db)
checker.scan_library(names, exts) checker.scan_library(names, exts)
for check in checks: for check in checks:
@ -1296,7 +1299,7 @@ def command_list_categories(args, dbpath):
if isbytestring(dbpath): if isbytestring(dbpath):
dbpath = dbpath.decode(preferred_encoding) dbpath = dbpath.decode(preferred_encoding)
db = LibraryDatabase2(dbpath) db = db_class()(dbpath)
category_data = db.get_categories() category_data = db.get_categories()
data = [] data = []
report_on = [c.strip() for c in opts.report.split(',') if c.strip()] report_on = [c.strip() for c in opts.report.split(',') if c.strip()]

View File

@ -10,14 +10,14 @@ import time, os
from threading import Thread from threading import Thread
from Queue import Empty from Queue import Empty
from calibre.library.database2 import LibraryDatabase2
from calibre.utils.ipc.server import Server from calibre.utils.ipc.server import Server
from calibre.utils.ipc.job import ParallelJob from calibre.utils.ipc.job import ParallelJob
def move_library(from_, to, notification = lambda x:x): def move_library(from_, to, notification=lambda x:x):
from calibre.db import get_db_loader
time.sleep(1) time.sleep(1)
old = LibraryDatabase2(from_) old = get_db_loader()[0](from_)
old.move_library_to(to, notification) old.move_library_to(to, notification)
return True return True

View File

@ -100,7 +100,7 @@ def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
def main(args=sys.argv): def main(args=sys.argv):
from calibre.library.database2 import LibraryDatabase2 from calibre.db import get_db_loader
parser = option_parser() parser = option_parser()
opts, args = parser.parse_args(args) opts, args = parser.parse_args(args)
if opts.daemonize and not iswindows: if opts.daemonize and not iswindows:
@ -116,7 +116,7 @@ def main(args=sys.argv):
print('No saved library path. Use the --with-library option' print('No saved library path. Use the --with-library option'
' to specify the path to the library you want to use.') ' to specify the path to the library you want to use.')
return 1 return 1
db = LibraryDatabase2(opts.with_library) db = get_db_loader()[0](opts.with_library)
server = LibraryServer(db, opts, show_tracebacks=opts.develop) server = LibraryServer(db, opts, show_tracebacks=opts.develop)
server.start() server.start()
return 0 return 0