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

svn: r18467
This commit is contained in:
Michiel Nauta 2011-11-20 09:33:31 +00:00
parent 7240d64278
commit 32b5905aea
4 changed files with 66 additions and 4 deletions

View File

@ -38,7 +38,8 @@ Declare constants used by database modules
__all__ = (
('DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO',
'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',
@ -53,6 +54,7 @@ DBEXT = ".db" # File extension to be used for database files
DBUNDOFN = "undo.db" # File name of 'undo' database
DBLOCKFN = "lock" # File name of lock file
DBRECOVFN = "need_recover" # File name of recovery file
BDBVERSFN = "bdbversion.txt"# File name of Berkeley DB version file
DBLOGNAME = ".Db" # Name of logger
DBMODE_R = "r" # Read-only 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 "
"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):
"""
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,
MediaObject, Repository, Note, Tag)
from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn,
DbTxn, BsddbBaseCursor, DbVersionError, DbEnvironmentError,
DbUpgradeRequiredError, find_surname, find_surname_name,
DbUndoBSDDB as DbUndo)
DbTxn, BsddbBaseCursor, BsddbDowngradeError, DbVersionError,
DbEnvironmentError, DbUpgradeRequiredError, find_surname,
find_surname_name, DbUndoBSDDB as DbUndo)
from gen.db.dbconst import *
from gen.utils.callback import Callback
from gen.updatecallback import UpdateCallback
@ -224,6 +224,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.secondary_connected = False
self.has_changed = False
self.brief_name = None
self.update_env_version = False
def catch_db_error(func):
"""
@ -349,6 +350,27 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
with BSDDBTxn(self.env, self.metadata) as txn:
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
def version_supported(self):
dbversion = self.metadata.get('version', default=0)
@ -400,6 +422,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.path = self.full_name
self.brief_name = os.path.basename(name)
self.__check_bdb_version(name)
# Set up database environment
self.env = db.DBEnv()
self.env.set_cachesize(0, DBCACHE)
@ -1099,6 +1123,15 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.undo_history_callback = 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:
clear_lock_file(self.get_save_path())
except IOError:

View File

@ -309,6 +309,9 @@ class DbLoader(CLIDbLoader):
self.dbstate.db.set_save_path(filename)
else:
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:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))