2007-06-12 Don Allingham <don@gramps-project.org>

* src/ViewManager.py: detection and recovery from db errors
	* src/GrampsDb/_GrampsDBDir.py: detection and recovery from db errors
	* src/GrampsDb/_GrampsBSDDB.py: detection and recovery from db errors
	* src/DbManager.py: detection and recovery from db errors
	* src/DbState.py: detection and recovery from db errors
	* src/ArgHandler.py: detection and recovery from db errors



svn: r8539
This commit is contained in:
Don Allingham
2007-06-12 23:31:40 +00:00
parent e05e6b4edd
commit c730a8f5c4
7 changed files with 319 additions and 167 deletions

View File

@ -1,3 +1,11 @@
2007-06-12 Don Allingham <don@gramps-project.org>
* src/ViewManager.py: detection and recovery from db errors
* src/GrampsDb/_GrampsDBDir.py: detection and recovery from db errors
* src/GrampsDb/_GrampsBSDDB.py: detection and recovery from db errors
* src/DbManager.py: detection and recovery from db errors
* src/DbState.py: detection and recovery from db errors
* src/ArgHandler.py: detection and recovery from db errors
2007-06-11 Don Allingham <don@gramps-project.org> 2007-06-11 Don Allingham <don@gramps-project.org>
* src/ViewManager.py: Improve backup strategy * src/ViewManager.py: Improve backup strategy
* src/GrampsDb/_GrampsDBDir.py: Improve backup strategy * src/GrampsDb/_GrampsDBDir.py: Improve backup strategy

View File

@ -449,7 +449,8 @@ class ArgHandler:
filetype = Mime.get_type(rf) filetype = Mime.get_type(rf)
self.vm.read_recent_file(rf, filetype) self.vm.read_recent_file(rf, filetype)
elif os.path.isdir(rf): elif os.path.isdir(rf):
if os.path.isfile(os.path.join(rf, "name.txt")): if os.path.isfile(os.path.join(rf, "name.txt")) and \
not os.path.isfile(os.path.join(rf,"need_recover"):
self.vm.read_recent_file(rf, 'x-directory/normal') self.vm.read_recent_file(rf, 'x-directory/normal')
#------------------------------------------------------------------------- #-------------------------------------------------------------------------

View File

@ -160,7 +160,7 @@ class DbManager:
if not node: if not node:
self.connect.set_sensitive(False) self.connect.set_sensitive(False)
self.rename.set_sensitive(False) self.rename.set_sensitive(False)
self.repair.set_sensitive(False) self.repair.hide()
self.remove.set_sensitive(False) self.remove.set_sensitive(False)
else: else:
if store.get_value(node, OPEN_COL): if store.get_value(node, OPEN_COL):
@ -168,7 +168,15 @@ class DbManager:
else: else:
self.connect.set_sensitive(True) self.connect.set_sensitive(True)
self.rename.set_sensitive(True) self.rename.set_sensitive(True)
self.repair.set_sensitive(True) if store.get_value(node, STOCK_COL) == gtk.STOCK_DIALOG_ERROR:
path = store.get_value(node, PATH_COL)
if os.path.isfile(os.path.join(path,"person.gbkp")):
self.repair.show()
else:
self.repair.hide()
else:
self.repair.hide()
self.remove.set_sensitive(True) self.remove.set_sensitive(True)
def build_interface(self): def build_interface(self):
@ -349,6 +357,8 @@ class DbManager:
db.load(dirname, None) db.load(dirname, None)
GrampsDbUtils.Backup.restore(db) GrampsDbUtils.Backup.restore(db)
db.close() db.close()
self.dbstate.no_database()
self.populate()
def new_db(self, obj): def new_db(self, obj):
""" """
@ -436,6 +446,8 @@ def icon_values(dirpath, active):
""" """
if dirpath == active: if dirpath == active:
return (True, gtk.STOCK_OPEN) return (True, gtk.STOCK_OPEN)
if os.path.isfile(os.path.join(dirpath,"need_recover")):
return (True, gtk.STOCK_DIALOG_ERROR)
else: else:
return (False, "") return (False, "")

View File

@ -45,9 +45,10 @@ class DbState(GrampsDBCallback):
just a place holder until a real DB is assigned. just a place holder until a real DB is assigned.
""" """
GrampsDBCallback.__init__(self) GrampsDBCallback.__init__(self)
self.db = GrampsDbBase() self.db = GrampsDbBase()
self.open = False self.open = False
self.active = None self.active = None
self.sighndl = None
def change_active_person(self, person): def change_active_person(self, person):
""" """

View File

@ -760,10 +760,11 @@ class GrampsBSDDB(GrampsDbBase, UpdateCallback):
> result_list = [i for i in find_backlink_handles(handle)] > result_list = [i for i in find_backlink_handles(handle)]
""" """
# Use the secondary index to locate all the reference_map entries # Use the secondary index to locate all the reference_map entries
# that include a reference to the object we are looking for. # that include a reference to the object we are looking for.
print "FIND BACKLINK"
referenced_cur = self.get_reference_map_referenced_cursor() referenced_cur = self.get_reference_map_referenced_cursor()
print "refcur", referenced_cur
try: try:
ret = referenced_cur.set(handle) ret = referenced_cur.set(handle)
@ -772,6 +773,7 @@ class GrampsBSDDB(GrampsDbBase, UpdateCallback):
while (ret is not None): while (ret is not None):
(key, data) = ret (key, data) = ret
print key, data
# data values are of the form: # data values are of the form:
# ((primary_object_class_name, primary_object_handle), # ((primary_object_class_name, primary_object_handle),
@ -794,8 +796,6 @@ class GrampsBSDDB(GrampsDbBase, UpdateCallback):
referenced_cur.close() referenced_cur.close()
return
def __delete_primary_from_reference_map(self, hndl, transaction, txn=None): def __delete_primary_from_reference_map(self, hndl, transaction, txn=None):
""" """
Remove all references to the primary object from the reference_map. Remove all references to the primary object from the reference_map.

View File

@ -53,6 +53,7 @@ import _GrampsDbConst as const
from _GrampsDbExceptions import FileVersionError from _GrampsDbExceptions import FileVersionError
from BasicUtils import UpdateCallback from BasicUtils import UpdateCallback
from _GrampsCursor import GrampsCursor from _GrampsCursor import GrampsCursor
import Errors
_MINVERSION = 9 _MINVERSION = 9
_DBVERSION = 13 _DBVERSION = 13
@ -82,6 +83,9 @@ REF_MAP = "reference_map"
REF_PRI = "primary_map" REF_PRI = "primary_map"
REF_REF = "referenced_map" REF_REF = "referenced_map"
DBERRS = (db.DBRunRecoveryError, db.DBAccessError, db.DBPageNotFoundError, db.DBInvalidArgError)
def find_surname(key,data): def find_surname(key,data):
return str(data[3][5]) return str(data[3][5])
@ -197,104 +201,130 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
dbmap.open(fname, table_name, dbtype, self.__open_flags(), 0666) dbmap.open(fname, table_name, dbtype, self.__open_flags(), 0666)
return dbmap return dbmap
def _all_handles(self,table): def __all_handles(self,table):
return table.keys(self.txn) return table.keys(self.txn)
def __log_error(self):
mypath = os.path.join(self.get_save_path(),"need_recover")
ofile = open(mypath, "w")
ofile.close()
def __get_cursor(self, table):
try:
return GrampsDBDirCursor(table, self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def get_person_cursor(self): def get_person_cursor(self):
return GrampsDBDirCursor(self.person_map, self.txn) return self.__get_cursor(self.person_map)
def get_family_cursor(self): def get_family_cursor(self):
return GrampsDBDirCursor(self.family_map, self.txn) return self.__get_cursor(self.family_map)
def get_event_cursor(self): def get_event_cursor(self):
return GrampsDBDirCursor(self.event_map, self.txn) return self.__get_cursor(self.event_map)
def get_place_cursor(self): def get_place_cursor(self):
return GrampsDBDirCursor(self.place_map, self.txn) return self.__get_cursor(self.place_map)
def get_source_cursor(self): def get_source_cursor(self):
return GrampsDBDirCursor(self.source_map, self.txn) return self.__get_cursor(self.source_map)
def get_media_cursor(self): def get_media_cursor(self):
return GrampsDBDirCursor(self.media_map, self.txn) return self.__get_cursor(self.media_map)
def get_repository_cursor(self): def get_repository_cursor(self):
return GrampsDBDirCursor(self.repository_map, self.txn) return self.__get_cursor(self.repository_map)
def get_note_cursor(self): def get_note_cursor(self):
return GrampsDBDirCursor(self.note_map, self.txn) return self.__get_cursor(self.note_map)
def has_person_handle(self,handle): def __has_handle(self, table, handle):
try:
return table.get(str(handle), txn=self.txn) != None
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def has_person_handle(self, handle):
""" """
returns True if the handle exists in the current Person database. returns True if the handle exists in the current Person database.
""" """
return self.person_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.person_map, handle)
def has_family_handle(self,handle): def has_family_handle(self,handle):
""" """
returns True if the handle exists in the current Family database. returns True if the handle exists in the current Family database.
""" """
return self.family_map.get(str(handle),txn=self.txn) != None return self.__has_handle(self.family_map, handle)
def has_object_handle(self,handle): def has_object_handle(self,handle):
""" """
returns True if the handle exists in the current MediaObjectdatabase. returns True if the handle exists in the current MediaObjectdatabase.
""" """
return self.media_map.get(str(handle),txn=self.txn) != None return self.__has_handle(self.media_map, handle)
def has_repository_handle(self,handle): def has_repository_handle(self,handle):
""" """
returns True if the handle exists in the current Repository database. returns True if the handle exists in the current Repository database.
""" """
return self.repository_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.repository_map, handle)
def has_note_handle(self,handle): def has_note_handle(self,handle):
""" """
returns True if the handle exists in the current Note database. returns True if the handle exists in the current Note database.
""" """
return self.note_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.note_map, handle)
def has_event_handle(self,handle): def has_event_handle(self,handle):
""" """
returns True if the handle exists in the current Repository database. returns True if the handle exists in the current Repository database.
""" """
return self.event_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.event_map, handle)
def has_place_handle(self,handle): def has_place_handle(self,handle):
""" """
returns True if the handle exists in the current Repository database. returns True if the handle exists in the current Repository database.
""" """
return self.place_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.place_map, handle)
def has_source_handle(self,handle): def has_source_handle(self, handle):
""" """
returns True if the handle exists in the current Repository database. returns True if the handle exists in the current Repository database.
""" """
return self.source_map.get(str(handle), txn=self.txn) != None return self.__has_handle(self.source_map, handle)
def get_raw_person_data(self,handle): def __get_raw_data(self, table, handle):
return self.person_map.get(str(handle), txn=self.txn) try:
return table.get(str(handle), txn=self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def get_raw_person_data(self, handle):
return self.__get_raw_data(self.person_map, handle)
def get_raw_family_data(self,handle): def get_raw_family_data(self,handle):
return self.family_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.family_map, handle)
def get_raw_object_data(self,handle): def get_raw_object_data(self,handle):
return self.media_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.media_map, handle)
def get_raw_place_data(self,handle): def get_raw_place_data(self,handle):
return self.place_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.place_map, handle)
def get_raw_event_data(self,handle): def get_raw_event_data(self,handle):
return self.event_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.event_map, handle)
def get_raw_source_data(self,handle): def get_raw_source_data(self,handle):
return self.source_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.source_map, handle)
def get_raw_repository_data(self,handle): def get_raw_repository_data(self,handle):
return self.repository_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.repository_map, handle)
def get_raw_note_data(self,handle): def get_raw_note_data(self,handle):
return self.note_map.get(str(handle), txn=self.txn) return self.__get_raw_data(self.note_map, handle)
# cursors for lookups in the reference_map for back reference # cursors for lookups in the reference_map for back reference
# lookups. The reference_map has three indexes: # lookups. The reference_map has three indexes:
@ -304,17 +334,36 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
# the main index is unique, the others allow duplicate entries. # the main index is unique, the others allow duplicate entries.
def get_reference_map_cursor(self): def get_reference_map_cursor(self):
return GrampsDBDirAssocCursor(self.reference_map, self.txn) try:
return GrampsDBDirAssocCursor(self.reference_map, self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def get_reference_map_primary_cursor(self): def get_reference_map_primary_cursor(self):
return GrampsDBDirDupCursor(self.reference_map_primary_map, self.txn) try:
return GrampsDBDirDupCursor(self.reference_map_primary_map, self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def get_reference_map_referenced_cursor(self): def get_reference_map_referenced_cursor(self):
return GrampsDBDirDupCursor(self.reference_map_referenced_map, self.txn) try:
return GrampsDBDirDupCursor(self.reference_map_referenced_map, self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
# These are overriding the GrampsDbBase's methods of saving metadata # These are overriding the GrampsDbBase's methods of saving metadata
# because we now have txn-capable metadata table # because we now have txn-capable metadata table
def set_default_person_handle(self, handle): def set_default_person_handle(self, handle):
try:
return self.__set_default_person_handle(handle)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __set_default_person_handle(self, handle):
"""sets the default Person to the passed instance""" """sets the default Person to the passed instance"""
if not self.readonly: if not self.readonly:
if self.UseTXN: if self.UseTXN:
@ -329,6 +378,13 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.metadata.sync() self.metadata.sync()
def get_default_person(self): def get_default_person(self):
try:
return self.__get_default_person()
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __get_default_person(self):
"""returns the default Person of the database""" """returns the default Person of the database"""
person = self.get_person_from_handle(self.get_default_handle()) person = self.get_person_from_handle(self.get_default_handle())
if person: if person:
@ -346,7 +402,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.metadata.sync() self.metadata.sync()
return None return None
def _set_column_order(self, col_list, name): def __set_column_order(self, col_list, name):
if self.metadata and not self.readonly: if self.metadata and not self.readonly:
if self.UseTXN: if self.UseTXN:
# Start transaction if needed # Start transaction if needed
@ -360,14 +416,29 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.metadata.sync() self.metadata.sync()
def version_supported(self): def version_supported(self):
dbversion = self.metadata.get('version',default=0) try:
return ((dbversion <= _DBVERSION) and (dbversion >= _MINVERSION)) dbversion = self.metadata.get('version',default=0)
return ((dbversion <= _DBVERSION) and (dbversion >= _MINVERSION))
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def need_upgrade(self): def need_upgrade(self):
dbversion = self.metadata.get('version',default=0) try:
return not self.readonly and dbversion < _DBVERSION dbversion = self.metadata.get('version',default=0)
return not self.readonly and dbversion < _DBVERSION
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def load(self, name, callback, mode="w"): def load(self, name, callback, mode="w"):
try:
return self.__load(name, callback, mode)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __load(self, name, callback, mode="w"):
if self.db_is_open: if self.db_is_open:
self.close() self.close()
@ -380,6 +451,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
callback(12) callback(12)
self.full_name = os.path.abspath(name) self.full_name = os.path.abspath(name)
self.path = self.full_name
self.brief_name = os.path.basename(name) self.brief_name = os.path.basename(name)
self.env = db.DBEnv() self.env = db.DBEnv()
@ -417,7 +489,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
# If we cannot work with this DB version, # If we cannot work with this DB version,
# it makes no sense to go further # it makes no sense to go further
if not self.version_supported: if not self.version_supported:
self._close_early() self.__close_early()
self.family_map = self.__open_table(self.full_name, FAMILY_TBL) self.family_map = self.__open_table(self.full_name, FAMILY_TBL)
self.place_map = self.__open_table(self.full_name, PLACES_TBL) self.place_map = self.__open_table(self.full_name, PLACES_TBL)
@ -490,9 +562,13 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
return 1 return 1
def load_from(self, other_database, filename, callback): def load_from(self, other_database, filename, callback):
self.load(filename,callback) try:
db_copy(other_database,self,callback) self.load(filename,callback)
return 1 db_copy(other_database,self,callback)
return 1
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __load_metadata(self): def __load_metadata(self):
# name display formats # name display formats
@ -660,7 +736,14 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.rmap_index = len(self.repository_map) self.rmap_index = len(self.repository_map)
self.nmap_index = len(self.note_map) self.nmap_index = len(self.note_map)
def rebuild_secondary(self,callback=None): def rebuild_secondary(self, callback=None):
try:
self.__rebuild_secondary(callback)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __rebuild_secondary(self, callback=None):
if self.readonly: if self.readonly:
return return
@ -683,8 +766,8 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
( self.reference_map_referenced_map, REF_REF), ( self.reference_map_referenced_map, REF_REF),
] ]
for (db, name) in items: for (database, name) in items:
db.close() database.close()
env = db.DB(self.env) env = db.DB(self.env)
env.remove(_mkname(self.full_name, name), name) env.remove(_mkname(self.full_name, name), name)
if callback: if callback:
@ -702,6 +785,13 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
callback(12) callback(12)
def find_backlink_handles(self, handle, include_classes=None): def find_backlink_handles(self, handle, include_classes=None):
try:
return self.__find_backlink_handles(handle, include_classes)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __find_backlink_handles(self, handle, include_classes=None):
""" """
Find all objects that hold a reference to the object handle. Find all objects that hold a reference to the object handle.
Returns an interator over a list of (class_name,handle) tuples. Returns an interator over a list of (class_name,handle) tuples.
@ -752,8 +842,6 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
referenced_cur.close() referenced_cur.close()
return
def __delete_primary_from_reference_map(self,handle,transaction,txn=None): def __delete_primary_from_reference_map(self,handle,transaction,txn=None):
""" """
Remove all references to the primary object from the reference_map. Remove all references to the primary object from the reference_map.
@ -789,9 +877,9 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
# Now that the cursor is closed, we can remove things # Now that the cursor is closed, we can remove things
for main_key in remove_list: for main_key in remove_list:
self._remove_reference(main_key,transaction,txn) self.__remove_reference(main_key,transaction,txn)
def _update_reference_map(self, obj, transaction, txn=None): def __update_reference_map(self, obj, transaction, txn=None):
""" """
If txn is given, then changes are written right away using txn. If txn is given, then changes are written right away using txn.
""" """
@ -859,17 +947,17 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
for (ref_class_name,ref_handle) in new_references: for (ref_class_name,ref_handle) in new_references:
data = ((CLASS_TO_KEY_MAP[obj.__class__.__name__],handle), data = ((CLASS_TO_KEY_MAP[obj.__class__.__name__],handle),
(CLASS_TO_KEY_MAP[ref_class_name],ref_handle),) (CLASS_TO_KEY_MAP[ref_class_name],ref_handle),)
self._add_reference((handle,ref_handle),data,transaction,txn) self.__add_reference((handle,ref_handle),data,transaction,txn)
# handle deletion of old references # handle deletion of old references
for (ref_class_name,ref_handle) in no_longer_required_references: for (ref_class_name,ref_handle) in no_longer_required_references:
try: try:
self._remove_reference((handle,ref_handle),transaction,txn) self.__remove_reference((handle,ref_handle),transaction,txn)
except: except:
# ignore missing old reference # ignore missing old reference
pass pass
def _remove_reference(self,key,transaction,txn=None): def __remove_reference(self,key,transaction,txn=None):
""" """
Removes the reference specified by the key, Removes the reference specified by the key,
preserving the change in the passed transaction. preserving the change in the passed transaction.
@ -884,7 +972,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
transaction.add(REFERENCE_KEY,str(key),old_data,None) transaction.add(REFERENCE_KEY,str(key),old_data,None)
transaction.reference_del.append(str(key)) transaction.reference_del.append(str(key))
def _add_reference(self,key,data,transaction,txn=None): def __add_reference(self,key,data,transaction,txn=None):
""" """
Adds the reference specified by the key and the data, Adds the reference specified by the key and the data,
preserving the change in the passed transaction. preserving the change in the passed transaction.
@ -902,6 +990,13 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
transaction.reference_add.append((str(key),data)) transaction.reference_add.append((str(key),data))
def reindex_reference_map(self,callback): def reindex_reference_map(self,callback):
try:
self.__reindex_reference_map(callback)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __reindex_reference_map(self,callback):
""" """
Reindex all primary records in the database. Reindex all primary records in the database.
This will be a slow process for large databases. This will be a slow process for large databases.
@ -981,7 +1076,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
the_txn = self.env.txn_begin() the_txn = self.env.txn_begin()
else: else:
the_txn = None the_txn = None
self._update_reference_map(obj,transaction,the_txn) self.__update_reference_map(obj,transaction,the_txn)
if not self.UseTXN: if not self.UseTXN:
self.reference_map.sync() self.reference_map.sync()
if the_txn: if the_txn:
@ -1002,9 +1097,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
find_referenced_handle,open_flags) find_referenced_handle,open_flags)
callback(6) callback(6)
return def __close_metadata(self):
def _close_metadata(self):
if not self.readonly: if not self.readonly:
if self.UseTXN: if self.UseTXN:
# Start transaction if needed # Start transaction if needed
@ -1077,7 +1170,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.metadata.close() self.metadata.close()
def _close_early(self): def __close_early(self):
""" """
Bail out if the incompatible version is discovered: Bail out if the incompatible version is discovered:
* close cleanly to not damage data/env * close cleanly to not damage data/env
@ -1095,13 +1188,20 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
"data between different database versions.") "data between different database versions.")
def close(self): def close(self):
try:
self.__close()
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __close(self):
if not self.db_is_open: if not self.db_is_open:
return return
if self.UseTXN: if self.UseTXN:
self.env.txn_checkpoint() self.env.txn_checkpoint()
self._close_metadata() self.__close_metadata()
self.name_group.close() self.name_group.close()
self.surnames.close() self.surnames.close()
self.id_trans.close() self.id_trans.close()
@ -1126,16 +1226,6 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.source_map.close() self.source_map.close()
self.media_map.close() self.media_map.close()
self.event_map.close() self.event_map.close()
# Attempt to clear log sequence numbers, to make database portable
# This will only work for python2.5 and higher
# Comment this our because it causes crashes.
# To reproduce the crash, create a new DB, import example.gramps, open and close the db a few times.
# try:
# self.env.lsn_reset(self.full_name)
# except AttributeError:
# pass
self.env.close() self.env.close()
try: try:
@ -1219,7 +1309,14 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
if not self.UseTXN: if not self.UseTXN:
self.event_map.sync() self.event_map.sync()
def set_name_group_mapping(self,name,group): def set_name_group_mapping(self, name, group):
try:
self.__set_name_group_mapping(name, group)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __set_name_group_mapping(self, name, group):
if not self.readonly: if not self.readonly:
if self.UseTXN: if self.UseTXN:
# Start transaction if needed # Start transaction if needed
@ -1239,8 +1336,12 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.emit('person-rebuild') self.emit('person-rebuild')
def build_surname_list(self): def build_surname_list(self):
self.surname_list = list(set(self.surnames.keys())) try:
self.sort_surname_list() self.surname_list = list(set(self.surnames.keys()))
self.sort_surname_list()
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def remove_from_surname_list(self,person): def remove_from_surname_list(self,person):
""" """
@ -1254,90 +1355,97 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.surname_list.remove(unicode(name)) self.surname_list.remove(unicode(name))
except ValueError: except ValueError:
pass pass
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def _get_obj_from_gramps_id(self,val,tbl,class_init,prim_tbl): def __get_obj_from_gramps_id(self,val,tbl,class_init,prim_tbl):
if tbl.has_key(str(val)): try:
data = tbl.get(str(val),txn=self.txn) if tbl.has_key(str(val)):
obj = class_init() data = tbl.get(str(val),txn=self.txn)
### FIXME: this is a dirty hack that works without no obj = class_init()
### sensible explanation. For some reason, for a readonly ### FIXME: this is a dirty hack that works without no
### database, secondary index returns a primary table key ### sensible explanation. For some reason, for a readonly
### corresponding to the data, not the data. ### database, secondary index returns a primary table key
if self.readonly: ### corresponding to the data, not the data.
tuple_data = prim_tbl.get(data,txn=self.txn) if self.readonly:
tuple_data = prim_tbl.get(data,txn=self.txn)
else:
tuple_data = pickle.loads(data)
obj.unserialize(tuple_data)
return obj
else: else:
tuple_data = pickle.loads(data) return None
obj.unserialize(tuple_data) except DBERRS, msg:
return obj self.__log_error()
else: raise Errors.DbError(msg)
return None
def get_person_from_gramps_id(self,val): def get_person_from_gramps_id(self,val):
""" """
Finds a Person in the database from the passed gramps' ID. Finds a Person in the database from the passed gramps' ID.
If no such Person exists, None is returned. If no such Person exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.id_trans,Person, return self.__get_obj_from_gramps_id(val, self.id_trans, Person,
self.person_map) self.person_map)
def get_family_from_gramps_id(self,val): def get_family_from_gramps_id(self,val):
""" """
Finds a Family in the database from the passed gramps' ID. Finds a Family in the database from the passed gramps' ID.
If no such Family exists, None is return. If no such Family exists, None is return.
""" """
return self._get_obj_from_gramps_id(val,self.fid_trans,Family, return self.__get_obj_from_gramps_id(val, self.fid_trans, Family,
self.family_map) self.family_map)
def get_event_from_gramps_id(self,val): def get_event_from_gramps_id(self,val):
""" """
Finds an Event in the database from the passed gramps' ID. Finds an Event in the database from the passed gramps' ID.
If no such Family exists, None is returned. If no such Family exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.eid_trans,Event, return self.__get_obj_from_gramps_id(val, self.eid_trans,Event,
self.event_map) self.event_map)
def get_place_from_gramps_id(self,val): def get_place_from_gramps_id(self,val):
""" """
Finds a Place in the database from the passed gramps' ID. Finds a Place in the database from the passed gramps' ID.
If no such Place exists, None is returned. If no such Place exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.pid_trans,Place, return self.__get_obj_from_gramps_id(val, self.pid_trans, Place,
self.place_map) self.place_map)
def get_source_from_gramps_id(self,val): def get_source_from_gramps_id(self,val):
""" """
Finds a Source in the database from the passed gramps' ID. Finds a Source in the database from the passed gramps' ID.
If no such Source exists, None is returned. If no such Source exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.sid_trans,Source, return self.__get_obj_from_gramps_id(val,self.sid_trans,Source,
self.source_map) self.source_map)
def get_object_from_gramps_id(self,val): def get_object_from_gramps_id(self,val):
""" """
Finds a MediaObject in the database from the passed gramps' ID. Finds a MediaObject in the database from the passed gramps' ID.
If no such MediaObject exists, None is returned. If no such MediaObject exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.oid_trans,MediaObject, return self.__get_obj_from_gramps_id(val,self.oid_trans,MediaObject,
self.media_map) self.media_map)
def get_repository_from_gramps_id(self,val): def get_repository_from_gramps_id(self,val):
""" """
Finds a Repository in the database from the passed gramps' ID. Finds a Repository in the database from the passed gramps' ID.
If no such Repository exists, None is returned. If no such Repository exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.rid_trans,Repository, return self.__get_obj_from_gramps_id(val,self.rid_trans,Repository,
self.repository_map) self.repository_map)
def get_note_from_gramps_id(self,val): def get_note_from_gramps_id(self,val):
""" """
Finds a Note in the database from the passed gramps' ID. Finds a Note in the database from the passed gramps' ID.
If no such Note exists, None is returned. If no such Note exists, None is returned.
""" """
return self._get_obj_from_gramps_id(val,self.nid_trans,Note, return self.__get_obj_from_gramps_id(val,self.nid_trans,Note,
self.note_map) self.note_map)
def __commit_base(self, obj, data_map, key, update_list, add_list, def __commit_base(self, obj, data_map, key, update_list, add_list,
transaction, change_time): transaction, change_time):
""" """
Commits the specified object to the database, storing the changes Commits the specified object to the database, storing the changes
as part of the transaction. as part of the transaction.
@ -1356,7 +1464,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
the_txn = self.env.txn_begin() the_txn = self.env.txn_begin()
else: else:
the_txn = None the_txn = None
self._update_reference_map(obj,transaction,txn=the_txn) self.__update_reference_map(obj,transaction,txn=the_txn)
data_map.put(handle,obj.serialize(),txn=the_txn) data_map.put(handle,obj.serialize(),txn=the_txn)
if not self.UseTXN: if not self.UseTXN:
data_map.sync() data_map.sync()
@ -1364,7 +1472,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
the_txn.commit() the_txn.commit()
old_data = None old_data = None
else: else:
self._update_reference_map(obj,transaction) self.__update_reference_map(obj,transaction)
old_data = data_map.get(handle,txn=self.txn) old_data = data_map.get(handle,txn=self.txn)
new_data = obj.serialize() new_data = obj.serialize()
transaction.add(key,handle,old_data,new_data) transaction.add(key,handle,old_data,new_data)
@ -1383,7 +1491,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
retlist.append(str(handle)) retlist.append(str(handle))
return retlist return retlist
def _get_from_handle(self, handle, class_type, data_map): def __get_from_handle(self, handle, class_type, data_map):
try: try:
data = data_map.get(str(handle),txn=self.txn) data = data_map.get(str(handle),txn=self.txn)
except: except:
@ -1398,7 +1506,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
return newobj return newobj
return None return None
def _find_from_handle(self,handle,transaction,class_type,dmap,add_func): def __find_from_handle(self, handle, transaction, class_type, dmap, add_func):
obj = class_type() obj = class_type()
handle = str(handle) handle = str(handle)
if dmap.has_key(handle): if dmap.has_key(handle):
@ -1409,7 +1517,14 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
add_func(obj,transaction) add_func(obj,transaction)
return obj return obj
def transaction_begin(self,msg="",batch=False,no_magic=False): def transaction_begin(self, msg="", batch=False, no_magic=False):
try:
return self.__transaction_begin(msg, batch, no_magic)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __transaction_begin(self, msg="", batch=False, no_magic=False):
""" """
Creates a new Transaction tied to the current UNDO database. The Creates a new Transaction tied to the current UNDO database. The
transaction has no effect until it is committed using the transaction has no effect until it is committed using the
@ -1441,7 +1556,14 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
return transaction return transaction
def transaction_commit(self,transaction,msg): def transaction_commit(self, transaction, msg):
try:
self.__transaction_commit(transaction, msg)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def __transaction_commit(self, transaction, msg):
# Start BSD DB transaction -- DBTxn # Start BSD DB transaction -- DBTxn
if self.UseTXN: if self.UseTXN:
@ -1496,49 +1618,63 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
self.txn = None self.txn = None
def undo(self,update_history=True): def undo(self,update_history=True):
print "Undoing it" try:
if self.UseTXN: if self.UseTXN:
self.txn = self.env.txn_begin() self.txn = self.env.txn_begin()
status = GrampsDbBase.undo(self,update_history) status = GrampsDbBase.undo(self,update_history)
if self.UseTXN: if self.UseTXN:
if status: if status:
self.txn.commit() self.txn.commit()
else: else:
self.txn.abort() self.txn.abort()
self.txn = None self.txn = None
return status return status
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def redo(self,update_history=True): def redo(self,update_history=True):
print "Redoing it" try:
if self.UseTXN: if self.UseTXN:
self.txn = self.env.txn_begin() self.txn = self.env.txn_begin()
status = GrampsDbBase.redo(self,update_history) status = GrampsDbBase.redo(self,update_history)
if self.UseTXN: if self.UseTXN:
if status: if status:
self.txn.commit() self.txn.commit()
else: else:
self.txn.abort() self.txn.abort()
self.txn = None self.txn = None
return status return status
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def undo_reference(self,data,handle): def undo_reference(self,data,handle):
if data == None: try:
self.reference_map.delete(handle,txn=self.txn) if data == None:
else: self.reference_map.delete(handle,txn=self.txn)
self.reference_map.put(handle,data,txn=self.txn) else:
self.reference_map.put(handle,data,txn=self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def undo_data(self,data,handle,db_map,signal_root): def undo_data(self,data,handle,db_map,signal_root):
if data == None: try:
self.emit(signal_root + '-delete',([handle],)) if data == None:
db_map.delete(handle,txn=self.txn) self.emit(signal_root + '-delete',([handle],))
else: db_map.delete(handle,txn=self.txn)
ex_data = db_map.get(handle,txn=self.txn)
if ex_data:
signal = signal_root + '-update'
else: else:
signal = signal_root + '-add' ex_data = db_map.get(handle,txn=self.txn)
db_map.put(handle,data,txn=self.txn) if ex_data:
self.emit(signal,([handle],)) signal = signal_root + '-update'
else:
signal = signal_root + '-add'
db_map.put(handle,data,txn=self.txn)
self.emit(signal,([handle],))
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def gramps_upgrade(self,callback=None): def gramps_upgrade(self,callback=None):
UpdateCallback.__init__(self,callback) UpdateCallback.__init__(self,callback)
@ -1556,8 +1692,8 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback):
class BdbTransaction(Transaction): class BdbTransaction(Transaction):
def __init__(self,msg,db,batch=False,no_magic=False): def __init__(self, msg, db, batch=False, no_magic=False):
Transaction.__init__(self,msg,db,batch,no_magic) Transaction.__init__(self, msg, db, batch, no_magic)
self.reference_del = [] self.reference_del = []
self.reference_add = [] self.reference_add = []

View File

@ -81,9 +81,6 @@ import GrampsDisplay
from GrampsDb import ProgressMonitor from GrampsDb import ProgressMonitor
import ProgressDialog import ProgressDialog
from bsddb.db import DBRunRecoveryError, DBAccessError, \
DBPageNotFoundError, DBInvalidArgError
def show_url(dialog, link, user_data): def show_url(dialog, link, user_data):
""" """
Sets the about dialog callback for showing the URL. Call the GrampsDisplay Sets the about dialog callback for showing the URL. Call the GrampsDisplay
@ -900,7 +897,7 @@ class ViewManager:
self.post_load() self.post_load()
def import_data(self, obj): def import_data(self, obj):
if self.state.db.db_is_open: if self.state.db.is_open():
self.db_loader.import_file() self.db_loader.import_file()
self.post_load() self.post_load()
@ -972,7 +969,7 @@ class ViewManager:
os.chdir(os.path.dirname(filename)) os.chdir(os.path.dirname(filename))
except: except:
print "could not change directory" print "could not change directory"
except DBRunRecoveryError, msg: except Errors.DbError, msg:
QuestionDialog.ErrorDialog( QuestionDialog.ErrorDialog(
_("Low level database corruption detected"), _("Low level database corruption detected"),
_("GRAMPS has detected a problem in the underlying " _("GRAMPS has detected a problem in the underlying "
@ -981,18 +978,15 @@ class ViewManager:
"the next time you open this database. If this " "the next time you open this database. If this "
"problem persists, create a new database, import " "problem persists, create a new database, import "
"from a backup database, and report the problem to " "from a backup database, and report the problem to "
"gramps-bugs@lists.sourceforge.net.")) "gramps-bugs@lists.sourceforge.net.") + "\n\n" + str(msg.value))
except (DBAccessError, DBPageNotFoundError, DBInvalidArgError), msg: self.state.no_database()
QuestionDialog.ErrorDialog(
_("Could not open file: %s") % filename,
str(msg[1]))
except Exception: except Exception:
LOG.error("Failed to open database.", exc_info=True) LOG.error("Failed to open database.", exc_info=True)
return True return True
def save_as_activate(self, obj): def save_as_activate(self, obj):
if self.state.db.db_is_open: if self.state.db.is_open():
(filename, filetype) = self.db_loader.save_as() (filename, filetype) = self.db_loader.save_as()
self.post_load_newdb(filename, filetype) self.post_load_newdb(filename, filetype)