5140: Import of Gramps XML file with cross table duplicate handles crashes

svn: r18036
This commit is contained in:
Michiel Nauta
2011-08-16 20:06:43 +00:00
parent 603238aeb0
commit 9dd5465216
2 changed files with 112 additions and 126 deletions

View File

@ -625,9 +625,10 @@ class GrampsParser(UpdateCallback):
"reporef": (self.start_reporef, self.stop_reporef),
"rname": (None, self.stop_rname),
}
self.grampsuri = re.compile(r"^gramps://(?P<object_class>[A-Z][a-z]+)/"
"handle/(?P<handle>\w+)$")
def inaugurate(self, handle, prim_obj, has_handle_func, add_func,
get_raw_obj_data):
def inaugurate(self, handle, target, prim_obj):
"""
Assign a handle (identity) to a primary object (and create it if it
doesn't exist yet) and add it to the database.
@ -641,28 +642,40 @@ class GrampsParser(UpdateCallback):
:param handle: The handle of the primary object, typically as read
directly from the XML attributes.
:type handle: str
:param target: Indicates the primary object type this handle relates to.
:type targe": str, identical to target attr of bookmarks.
:param prim_obj: template of the primary object that is to be created.
:type prim_obj: Either an empty instance of a primary object or the
class object of a primary object.
:param has_handle_func: function to determine if the database contains
a given handle for a specific object type.
:type has_handle_func: func
:param add_func: function to add a primary object of a specific type
to the database.
:type add_func: func
:param get_raw_obj_data: function to read the content of a primary
object from the db in serialized form.
:type get_raw_obj_data: func
:returns: The handle of the primary object.
:rtype: str
"""
handle = str(handle.replace('_', ''))
if handle in self.import_handles:
handle = self.import_handles[handle]
if (handle in self.import_handles and
target in self.import_handles[handle]):
handle = self.import_handles[handle][target]
if not callable(prim_obj):
# This method is called by a start_<primary_object> method.
get_raw_obj_data = {"person": self.db.get_raw_person_data,
"family": self.db.get_raw_family_data,
"event": self.db.get_raw_event_data,
"place": self.db.get_raw_place_data,
"source": self.db.get_raw_source_data,
"repository": self.db.get_raw_repository_data,
"media": self.db.get_raw_object_data,
"note": self.db.get_raw_note_data,
"tag": self.db.get_raw_tag_data}[target]
raw = get_raw_obj_data(handle)
prim_obj.unserialize(raw)
return handle
elif handle in self.import_handles:
LOG.warn("The file you import contains duplicate handles "
"which is illegal and being fixed now.")
orig_handle = handle
handle = Utils.create_id()
while handle in self.import_handles:
handle = Utils.create_id()
self.import_handles[orig_handle][target] = handle
else:
orig_handle = handle
if self.replace_import_handle:
@ -670,16 +683,33 @@ class GrampsParser(UpdateCallback):
while handle in self.import_handles:
handle = Utils.create_id()
else:
has_handle_func = {"person": self.db.has_person_handle,
"family": self.db.has_family_handle,
"event": self.db.has_event_handle,
"place": self.db.has_place_handle,
"source": self.db.has_source_handle,
"repository": self.db.has_repository_handle,
"media": self.db.has_object_handle,
"note": self.db.has_note_handle,
"tag": self.db.has_tag_handle}[target]
while has_handle_func(handle):
handle = Utils.create_id()
self.import_handles[orig_handle] = handle
if callable(prim_obj): # method is called by a reference
prim_obj = prim_obj()
prim_obj.set_handle(handle)
if add_func == self.db.add_tag:
add_func(prim_obj, self.trans)
else:
add_func(prim_obj, self.trans, set_gid=False)
self.import_handles[orig_handle] = {target: handle}
if callable(prim_obj): # method is called by a reference
prim_obj = prim_obj()
prim_obj.set_handle(handle)
if target == "tag":
self.db.add_tag(prim_obj, self.trans)
else:
add_func = {"person": self.db.add_person,
"family": self.db.add_family,
"event": self.db.add_event,
"place": self.db.add_place,
"source": self.db.add_source,
"repository": self.db.add_repository,
"media": self.db.add_object,
"note": self.db.add_note}[target]
add_func(prim_obj, self.trans, set_gid=False)
return handle
def inaugurate_id(self, id_, key, prim_obj):
@ -954,10 +984,7 @@ class GrampsParser(UpdateCallback):
Add a family reference to the LDS ordinance currently processed.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Family,
self.db.has_family_handle,
self.db.add_family,
self.db.get_raw_family_data)
handle = self.inaugurate(attrs['hlink'], "family", gen.lib.Family)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), FAMILY_KEY,
gen.lib.Family)
@ -967,10 +994,7 @@ class GrampsParser(UpdateCallback):
"""A reference to a place in an object: event or lds_ord
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Place,
self.db.has_place_handle,
self.db.add_place,
self.db.get_raw_place_data)
handle = self.inaugurate(attrs['hlink'], "place", gen.lib.Place)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), PLACE_KEY,
gen.lib.Place)
@ -989,10 +1013,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_place_handle(orig_handle))
self.inaugurate(orig_handle, self.placeobj,
self.db.has_place_handle,
self.db.add_place,
self.db.get_raw_place_data)
self.inaugurate(orig_handle, "place", self.placeobj)
gramps_id = self.legalize_id(attrs.get('id'), PLACE_KEY,
self.pidswap, self.db.pid2user_format,
self.db.find_next_place_gramps_id)
@ -1056,8 +1077,7 @@ class GrampsParser(UpdateCallback):
person = gen.lib.Person()
if 'hlink' in attrs:
self.inaugurate(attrs['hlink'], person, self.db.has_person_handle,
self.db.add_person, self.db.get_raw_person_data)
self.inaugurate(attrs['hlink'], "person", person)
elif 'ref' in attrs:
self.inaugurate_id(attrs['ref'], PERSON_KEY, person)
else:
@ -1100,10 +1120,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_event_handle(orig_handle))
self.inaugurate(orig_handle, self.event,
self.db.has_event_handle,
self.db.add_event,
self.db.get_raw_event_data)
self.inaugurate(orig_handle, "event", self.event)
gramps_id = self.legalize_id(attrs.get('id'), EVENT_KEY,
self.eidswap, self.db.eid2user_format,
self.db.find_next_event_gramps_id)
@ -1124,10 +1141,7 @@ class GrampsParser(UpdateCallback):
"""
self.eventref = gen.lib.EventRef()
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Event,
self.db.has_event_handle,
self.db.add_event,
self.db.get_raw_event_data)
handle = self.inaugurate(attrs['hlink'], "event", gen.lib.Event)
else: # there is no old style XML
raise GrampsImportError(_("The Gramps Xml you are trying to "
"import is malformed."), _("Any event reference must have a "
@ -1194,10 +1208,8 @@ class GrampsParser(UpdateCallback):
# Old XML. Can be either handle or id reference
# and this is guaranteed to be a person bookmark
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person",
gen.lib.Person)
else:
handle = self.inaugurate_id(attrs.get('ref'), PERSON_KEY,
gen.lib.Person)
@ -1206,7 +1218,7 @@ class GrampsParser(UpdateCallback):
# This is new XML, so we are guaranteed to have a handle ref
handle = attrs['hlink'].replace('_', '')
handle = self.import_handles[handle]
handle = self.import_handles[handle][target]
# Due to pre 2.2.9 bug, bookmarks might be handle of other object
# Make sure those are filtered out.
# Bookmarks are at end, so all handle must exist before we do bookmrks
@ -1279,10 +1291,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_person_handle(orig_handle))
self.inaugurate(orig_handle, self.person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
self.inaugurate(orig_handle, "person", self.person)
gramps_id = self.legalize_id(attrs.get('id'), PERSON_KEY,
self.idswap, self.db.id2user_format,
self.db.find_next_person_gramps_id)
@ -1303,10 +1312,7 @@ class GrampsParser(UpdateCallback):
Store the home person of the database.
"""
if 'home' in attrs:
handle = self.inaugurate(attrs['home'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['home'], "person", gen.lib.Person)
self.home = handle
def start_father(self, attrs):
@ -1314,10 +1320,7 @@ class GrampsParser(UpdateCallback):
Add a father reference to the family currently processed.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person", gen.lib.Person)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), PERSON_KEY,
gen.lib.Person)
@ -1328,10 +1331,7 @@ class GrampsParser(UpdateCallback):
Add a mother reference to the family currently processed.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person", gen.lib.Person)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), PERSON_KEY,
gen.lib.Person)
@ -1345,10 +1345,7 @@ class GrampsParser(UpdateCallback):
frel and mrel belonged to the "childof" tag
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person", gen.lib.Person)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), PERSON_KEY,
gen.lib.Person)
@ -1366,10 +1363,7 @@ class GrampsParser(UpdateCallback):
belong to the "childref" tag under family.
"""
self.childref = gen.lib.ChildRef()
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person", gen.lib.Person)
self.childref.ref = handle
self.childref.private = bool(attrs.get('priv'))
@ -1392,10 +1386,7 @@ class GrampsParser(UpdateCallback):
"""
self.personref = gen.lib.PersonRef()
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Person,
self.db.has_person_handle,
self.db.add_person,
self.db.get_raw_person_data)
handle = self.inaugurate(attrs['hlink'], "person", gen.lib.Person)
else: # there is no old style XML
raise GrampsImportError(_("The Gramps Xml you are trying to "
"import is malformed."), _("Any person reference must have a "
@ -1431,10 +1422,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_family_handle(orig_handle))
self.inaugurate(orig_handle, self.family,
self.db.has_family_handle,
self.db.add_family,
self.db.get_raw_family_data)
self.inaugurate(orig_handle, "family", self.family)
gramps_id = self.legalize_id(attrs.get('id'), FAMILY_KEY,
self.fidswap, self.db.fid2user_format,
self.db.find_next_family_gramps_id)
@ -1478,10 +1466,7 @@ class GrampsParser(UpdateCallback):
person is a child.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Family,
self.db.has_family_handle,
self.db.add_family,
self.db.get_raw_family_data)
handle = self.inaugurate(attrs['hlink'], "family", gen.lib.Family)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), FAMILY_KEY,
gen.lib.Family)
@ -1510,10 +1495,7 @@ class GrampsParser(UpdateCallback):
person is a parent.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Family,
self.db.has_family_handle,
self.db.add_family,
self.db.get_raw_family_data)
handle = self.inaugurate(attrs['hlink'], "family", gen.lib.Family)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), FAMILY_KEY,
gen.lib.Family)
@ -1613,8 +1595,7 @@ class GrampsParser(UpdateCallback):
# Tag defintion
self.tag = gen.lib.Tag()
self.inaugurate(attrs['handle'], self.tag, self.db.has_tag_handle,
self.db.add_tag, self.db.get_raw_tag_data)
self.inaugurate(attrs['handle'], "tag", self.tag)
self.tag.change = int(attrs.get('change', self.change))
self.info.add('new-object', TAG_KEY, self.tag)
self.tag.set_name(attrs['name'])
@ -1626,9 +1607,7 @@ class GrampsParser(UpdateCallback):
"""
Tag reference in a primary object.
"""
handle = self.inaugurate(attrs['hlink'], gen.lib.Tag,
self.db.has_tag_handle,
self.db.add_tag, self.db.get_raw_tag_data)
handle = self.inaugurate(attrs['hlink'], "tag", gen.lib.Tag)
if self.person:
self.person.add_tag(handle)
@ -1660,10 +1639,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_note_handle(orig_handle))
self.inaugurate(orig_handle, self.note,
self.db.has_note_handle,
self.db.add_note,
self.db.get_raw_note_data)
self.inaugurate(orig_handle, "note", self.note)
gramps_id = self.legalize_id(attrs.get('id'), NOTE_KEY,
self.nidswap, self.db.nid2user_format,
self.db.find_next_note_gramps_id)
@ -1763,10 +1739,7 @@ class GrampsParser(UpdateCallback):
Add a note reference to the object currently processed.
"""
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Note,
self.db.has_note_handle,
self.db.add_note,
self.db.get_raw_note_data)
handle = self.inaugurate(attrs['hlink'], "note", gen.lib.Note)
else:
raise GrampsImportError(_("The Gramps Xml you are trying to "
"import is malformed."), _("Any note reference must have a "
@ -1817,10 +1790,7 @@ class GrampsParser(UpdateCallback):
"""
self.source_ref = gen.lib.SourceRef()
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Source,
self.db.has_source_handle,
self.db.add_source,
self.db.get_raw_source_data)
handle = self.inaugurate(attrs['hlink'], "source", gen.lib.Source)
else:
handle = self.inaugurate_id(attrs.get('ref'), SOURCE_KEY,
gen.lib.Source)
@ -1866,10 +1836,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_source_handle(orig_handle))
self.inaugurate(orig_handle, self.source,
self.db.has_source_handle,
self.db.add_source,
self.db.get_raw_source_data)
self.inaugurate(orig_handle, "source", self.source)
gramps_id = self.legalize_id(attrs.get('id'), SOURCE_KEY,
self.sidswap, self.db.sid2user_format,
self.db.find_next_source_gramps_id)
@ -1890,10 +1857,8 @@ class GrampsParser(UpdateCallback):
"""
self.reporef = gen.lib.RepoRef()
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.Repository,
self.db.has_repository_handle,
self.db.add_repository,
self.db.get_raw_repository_data)
handle = self.inaugurate(attrs['hlink'], "repository",
gen.lib.Repository)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), REPOSITORY_KEY,
gen.lib.Repository)
@ -1912,10 +1877,8 @@ class GrampsParser(UpdateCallback):
"""
self.objref = gen.lib.MediaRef()
if 'hlink' in attrs:
handle = self.inaugurate(attrs['hlink'], gen.lib.MediaObject,
self.db.has_object_handle,
self.db.add_object,
self.db.get_raw_object_data)
handle = self.inaugurate(attrs['hlink'], "media",
gen.lib.MediaObject)
else: # old style XML
handle = self.inaugurate_id(attrs.get('ref'), MEDIA_KEY,
gen.lib.MediaObject)
@ -1949,10 +1912,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_object_handle(orig_handle))
self.inaugurate(orig_handle, self.object,
self.db.has_object_handle,
self.db.add_object,
self.db.get_raw_object_data)
self.inaugurate(orig_handle, "media", self.object)
gramps_id = self.legalize_id(attrs.get('id'), MEDIA_KEY,
self.oidswap, self.db.oid2user_format,
self.db.find_next_object_gramps_id)
@ -1986,10 +1946,7 @@ class GrampsParser(UpdateCallback):
orig_handle = attrs['handle'].replace('_', '')
is_merge_candidate = (self.replace_import_handle and
self.db.has_repository_handle(orig_handle))
self.inaugurate(orig_handle, self.repo,
self.db.has_repository_handle,
self.db.add_repository,
self.db.get_raw_repository_data)
self.inaugurate(orig_handle, "repository", self.repo)
gramps_id = self.legalize_id(attrs.get('id'), REPOSITORY_KEY,
self.ridswap, self.db.rid2user_format,
self.db.find_next_repository_gramps_id)