5368: Loading familytree with downgraded Berkeley db should generate expressive error

svn: r18468
This commit is contained in:
Michiel Nauta 2011-11-20 09:34:30 +00:00
parent 76d422feb6
commit f58ec21ec8
4 changed files with 66 additions and 4 deletions

View File

@ -38,7 +38,8 @@ Declare constants used by database modules
__all__ = ( __all__ = (
('DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO', ('DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO',
'DBEXT', 'DBMODE_R', 'DBMODE_W', 'DBUNDOFN', 'DBLOCKFN', 'DBEXT', 'DBMODE_R', 'DBMODE_W', 'DBUNDOFN', 'DBLOCKFN',
'DBRECOVFN', 'DBLOGNAME', 'DBFLAGS_O', 'DBFLAGS_R', 'DBFLAGS_D', 'DBRECOVFN','BDBVERSFN', 'DBLOGNAME', 'DBFLAGS_O', 'DBFLAGS_R',
'DBFLAGS_D',
) + ) +
('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'EVENT_KEY', ('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'EVENT_KEY',
@ -53,6 +54,7 @@ DBEXT = ".db" # File extension to be used for database files
DBUNDOFN = "undo.db" # File name of 'undo' database DBUNDOFN = "undo.db" # File name of 'undo' database
DBLOCKFN = "lock" # File name of lock file DBLOCKFN = "lock" # File name of lock file
DBRECOVFN = "need_recover" # File name of recovery file DBRECOVFN = "need_recover" # File name of recovery file
BDBVERSFN = "bdbversion.txt"# File name of Berkeley DB version file
DBLOGNAME = ".Db" # Name of logger DBLOGNAME = ".Db" # Name of logger
DBMODE_R = "r" # Read-only access DBMODE_R = "r" # Read-only access
DBMODE_W = "w" # Full Reaw/Write access DBMODE_W = "w" # Full Reaw/Write access

View File

@ -79,6 +79,30 @@ class DbVersionError(Exception):
"Gramps.\nPlease upgrade to the corresponding version or use " "Gramps.\nPlease upgrade to the corresponding version or use "
"XML for porting data between different database versions.") "XML for porting data between different database versions.")
class BsddbDowngradeError(Exception):
"""
Error used to report that the Berkeley database used to create the family
tree is of a version that is too new to be supported by the current version.
"""
def __init__(self, env_version, bdb_version):
Exception.__init__(self)
self.env_version = str(env_version)
self.bdb_version = str(bdb_version)
def __str__(self):
return _('Gramps stores its data in a Berkeley Database. '
'The family tree you try to load was created with version '
'%(env_version)s of the Berkeley DB. However, the Gramps '
'version in use right now employs version %(bdb_version)s '
'of the Berkeley DB. So you are trying to load data created '
'in a newer format into an older program; this is bound to '
'fail. The right approach in this case is to use XML export '
'and import. So try to open the family tree on that computer '
'with that software that created the family tree, export it '
'to XML and load that XML into the version of Gramps you '
'intend to use.') % {'env_version': self.env_version,
'bdb_version': self.bdb_version}
class DbEnvironmentError(Exception): class DbEnvironmentError(Exception):
""" """
Error used to report that the database 'environment' could not be opened. Error used to report that the database 'environment' could not be opened.

View File

@ -56,9 +56,9 @@ else:
from gen.lib import (GenderStats, Person, Family, Event, Place, Source, from gen.lib import (GenderStats, Person, Family, Event, Place, Source,
MediaObject, Repository, Note, Tag) MediaObject, Repository, Note, Tag)
from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn, from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn,
DbTxn, BsddbBaseCursor, DbVersionError, DbEnvironmentError, DbTxn, BsddbBaseCursor, BsddbDowngradeError, DbVersionError,
DbUpgradeRequiredError, find_surname, find_surname_name, DbEnvironmentError, DbUpgradeRequiredError, find_surname,
DbUndoBSDDB as DbUndo) find_surname_name, DbUndoBSDDB as DbUndo)
from gen.db.dbconst import * from gen.db.dbconst import *
from gen.utils.callback import Callback from gen.utils.callback import Callback
from gen.updatecallback import UpdateCallback from gen.updatecallback import UpdateCallback
@ -224,6 +224,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.secondary_connected = False self.secondary_connected = False
self.has_changed = False self.has_changed = False
self.brief_name = None self.brief_name = None
self.update_env_version = False
def catch_db_error(func): def catch_db_error(func):
""" """
@ -349,6 +350,27 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
with BSDDBTxn(self.env, self.metadata) as txn: with BSDDBTxn(self.env, self.metadata) as txn:
txn.put('mediapath', path) txn.put('mediapath', path)
def __check_bdb_version(self, name):
"""Older version of Berkeley DB can't read data created by a newer
version."""
bdb_version = db.version()
env_version = (0, 0, 0)
versionpath = os.path.join(self.path, BDBVERSFN)
try:
with open(versionpath, "r") as version_file:
env_version = version_file.read().strip()
env_version = tuple(map(int, env_version[1:-1].split(', ')))
except:
# Just assume that the Berkeley DB version is OK.
pass
if (env_version[0] > bdb_version[0]) or \
(env_version[0] == bdb_version[0] and
env_version[1] > bdb_version[1]):
clear_lock_file(name)
raise BsddbDowngradeError(env_version, bdb_version)
elif env_version != bdb_version and not self.readonly:
self.update_env_version = True
@catch_db_error @catch_db_error
def version_supported(self): def version_supported(self):
dbversion = self.metadata.get('version', default=0) dbversion = self.metadata.get('version', default=0)
@ -400,6 +422,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.path = self.full_name self.path = self.full_name
self.brief_name = os.path.basename(name) self.brief_name = os.path.basename(name)
self.__check_bdb_version(name)
# Set up database environment # Set up database environment
self.env = db.DBEnv() self.env = db.DBEnv()
self.env.set_cachesize(0, DBCACHE) self.env.set_cachesize(0, DBCACHE)
@ -1099,6 +1123,15 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.undo_history_callback = None self.undo_history_callback = None
self.undodb = None self.undodb = None
if self.update_env_version:
versionpath = os.path.join(self.path, BDBVERSFN)
try:
with open(versionpath, "w") as version_file:
version_file.write(str(db.version()))
except:
# Storing the version of Berkeley Db is not really vital.
pass
try: try:
clear_lock_file(self.get_save_path()) clear_lock_file(self.get_save_path())
except IOError: except IOError:

View File

@ -309,6 +309,9 @@ class DbLoader(CLIDbLoader):
self.dbstate.db.set_save_path(filename) self.dbstate.db.set_save_path(filename)
else: else:
self.dbstate.no_database() self.dbstate.no_database()
except gen.db.exceptions.BsddbDowngradeError, msg:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))
except gen.db.exceptions.DbVersionError, msg: except gen.db.exceptions.DbVersionError, msg:
self.dbstate.no_database() self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg)) self._errordialog( _("Cannot open database"), str(msg))