Guarantee order on db emits Partial fix #10068
This commit is contained in:
parent
a1205009f4
commit
48eb507f98
@ -44,7 +44,8 @@ import glob
|
||||
from . import (DbReadBase, DbWriteBase, DbUndo, DBLOGNAME, DBUNDOFN,
|
||||
KEY_TO_CLASS_MAP, REFERENCE_KEY, PERSON_KEY, FAMILY_KEY,
|
||||
CITATION_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY, PLACE_KEY,
|
||||
REPOSITORY_KEY, NOTE_KEY, TAG_KEY, TXNADD, TXNDEL)
|
||||
REPOSITORY_KEY, NOTE_KEY, TAG_KEY, TXNADD, TXNUPD, TXNDEL,
|
||||
KEY_TO_NAME_MAP)
|
||||
from ..errors import HandleError
|
||||
from ..utils.callback import Callback
|
||||
from ..updatecallback import UpdateCallback
|
||||
@ -132,6 +133,8 @@ class DbGenericUndo(DbUndo):
|
||||
transaction = txn
|
||||
db = self.db
|
||||
subitems = transaction.get_recnos()
|
||||
# sigs[obj_type][trans_type]
|
||||
sigs = [[[] for trans_type in range(3)] for key in range(11)]
|
||||
|
||||
# Process all records in the transaction
|
||||
try:
|
||||
@ -144,14 +147,10 @@ class DbGenericUndo(DbUndo):
|
||||
self.undo_reference(new_data, handle)
|
||||
else:
|
||||
self.undo_data(new_data, handle, key)
|
||||
sigs[key][trans_type].append(handle)
|
||||
# now emit the signals
|
||||
for record_id in subitems:
|
||||
(key, trans_type, handle, old_data, new_data) = \
|
||||
pickle.loads(self.undodb[record_id])
|
||||
self.undo_sigs(sigs, False)
|
||||
|
||||
if key != REFERENCE_KEY:
|
||||
self.undo_signals(trans_type, handle,
|
||||
db.emit, SIGBASE[key], False)
|
||||
self.db._txn_commit()
|
||||
except:
|
||||
self.db._txn_abort()
|
||||
@ -183,6 +182,8 @@ class DbGenericUndo(DbUndo):
|
||||
transaction = txn
|
||||
db = self.db
|
||||
subitems = transaction.get_recnos(reverse=True)
|
||||
# sigs[obj_type][trans_type]
|
||||
sigs = [[[] for trans_type in range(3)] for key in range(11)]
|
||||
|
||||
# Process all records in the transaction
|
||||
try:
|
||||
@ -195,14 +196,10 @@ class DbGenericUndo(DbUndo):
|
||||
self.undo_reference(old_data, handle)
|
||||
else:
|
||||
self.undo_data(old_data, handle, key)
|
||||
sigs[key][trans_type].append(handle)
|
||||
# now emit the signals
|
||||
for record_id in subitems:
|
||||
(key, trans_type, handle, old_data, new_data) = \
|
||||
pickle.loads(self.undodb[record_id])
|
||||
self.undo_sigs(sigs, True)
|
||||
|
||||
if key != REFERENCE_KEY:
|
||||
self.undo_signals(trans_type, handle,
|
||||
db.emit, SIGBASE[key], True)
|
||||
self.db._txn_commit()
|
||||
except:
|
||||
self.db._txn_abort()
|
||||
@ -257,19 +254,34 @@ class DbGenericUndo(DbUndo):
|
||||
obj = self.db._get_table_func(cls)["class_func"].create(data)
|
||||
self.db._update_secondary_values(obj)
|
||||
|
||||
def undo_signals(self, trans_type, handle, emit, signal_root, reverse):
|
||||
def undo_sigs(self, sigs, undo):
|
||||
"""
|
||||
Helper method to undo/redo the changes made
|
||||
Helper method to undo/redo the signals for changes made
|
||||
We want to do deletes and adds first
|
||||
Note that if 'undo' we swap emits
|
||||
"""
|
||||
if ((not reverse) and trans_type == TXNADD) \
|
||||
or (reverse and trans_type == TXNDEL):
|
||||
typ = '-add'
|
||||
elif not reverse and trans_type == TXNDEL \
|
||||
or reverse and trans_type == TXNADD:
|
||||
typ = '-delete'
|
||||
else: # TXNUPD
|
||||
typ = '-update'
|
||||
emit(signal_root + typ, ([handle],))
|
||||
for trans_type in [TXNDEL, TXNADD, TXNUPD]:
|
||||
for obj_type in range(11):
|
||||
handles = sigs[obj_type][trans_type]
|
||||
if handles:
|
||||
if not undo and trans_type == TXNDEL \
|
||||
or undo and trans_type == TXNADD:
|
||||
typ = '-delete'
|
||||
else:
|
||||
# don't update a handle if its been deleted, and note
|
||||
# that 'deleted' handles are in the 'add' list if we
|
||||
# are undoing
|
||||
handles = [handle for handle in handles
|
||||
if handle not in
|
||||
sigs[obj_type][TXNADD if undo else TXNDEL]]
|
||||
if ((not undo) and trans_type == TXNADD) \
|
||||
or (undo and trans_type == TXNDEL):
|
||||
typ = '-add'
|
||||
else: # TXNUPD
|
||||
typ = '-update'
|
||||
if handles:
|
||||
self.db.emit(KEY_TO_NAME_MAP[obj_type] + typ,
|
||||
(handles,))
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, iterator):
|
||||
|
@ -51,7 +51,8 @@ _ = glocale.translation.gettext
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.db.dbconst import *
|
||||
from gramps.gen.db.dbconst import (REFERENCE_KEY, KEY_TO_NAME_MAP, TXNDEL,
|
||||
TXNADD, TXNUPD)
|
||||
from . import BSDDBTxn
|
||||
from gramps.gen.errors import DbError
|
||||
|
||||
@ -226,8 +227,9 @@ class DbUndo:
|
||||
txn = self.undoq.pop()
|
||||
self.redoq.append(txn)
|
||||
transaction = txn
|
||||
db = self.db
|
||||
subitems = transaction.get_recnos(reverse=True)
|
||||
# sigs[obj_type][trans_type]
|
||||
sigs = [[[] for trans_type in range(3)] for key in range(11)]
|
||||
|
||||
# Process all records in the transaction
|
||||
for record_id in subitems:
|
||||
@ -238,29 +240,25 @@ class DbUndo:
|
||||
self.undo_reference(old_data, handle, self.mapbase[key])
|
||||
else:
|
||||
self.undo_data(old_data, handle, self.mapbase[key])
|
||||
handle = handle.decode('utf-8')
|
||||
sigs[key][trans_type].append(handle)
|
||||
# now emit the signals
|
||||
for record_id in subitems:
|
||||
(key, trans_type, handle, old_data, new_data) = \
|
||||
pickle.loads(self.undodb[record_id])
|
||||
|
||||
if key != REFERENCE_KEY:
|
||||
self.undo_signals(trans_type, handle,
|
||||
db.emit, _SIGBASE[key], True)
|
||||
self.undo_sigs(sigs, True)
|
||||
|
||||
# Notify listeners
|
||||
if db.undo_callback:
|
||||
if self.db.undo_callback:
|
||||
if self.undo_count > 0:
|
||||
db.undo_callback(_("_Undo %s")
|
||||
% self.undoq[-1].get_description())
|
||||
self.db.undo_callback(_("_Undo %s")
|
||||
% self.undoq[-1].get_description())
|
||||
else:
|
||||
db.undo_callback(None)
|
||||
self.db.undo_callback(None)
|
||||
|
||||
if db.redo_callback:
|
||||
db.redo_callback(_("_Redo %s")
|
||||
% transaction.get_description())
|
||||
if self.db.redo_callback:
|
||||
self.db.redo_callback(_("_Redo %s")
|
||||
% transaction.get_description())
|
||||
|
||||
if update_history and db.undo_history_callback:
|
||||
db.undo_history_callback()
|
||||
if update_history and self.db.undo_history_callback:
|
||||
self.db.undo_history_callback()
|
||||
return True
|
||||
|
||||
@undoredo
|
||||
@ -272,8 +270,9 @@ class DbUndo:
|
||||
txn = self.redoq.pop()
|
||||
self.undoq.append(txn)
|
||||
transaction = txn
|
||||
db = self.db
|
||||
subitems = transaction.get_recnos()
|
||||
# sigs[obj_type][trans_type]
|
||||
sigs = [[[] for trans_type in range(3)] for key in range(11)]
|
||||
|
||||
# Process all records in the transaction
|
||||
for record_id in subitems:
|
||||
@ -284,29 +283,26 @@ class DbUndo:
|
||||
self.undo_reference(new_data, handle, self.mapbase[key])
|
||||
else:
|
||||
self.undo_data(new_data, handle, self.mapbase[key])
|
||||
# Process all signals in the transaction
|
||||
for record_id in subitems:
|
||||
(key, trans_type, handle, old_data, new_data) = \
|
||||
pickle.loads(self.undodb[record_id])
|
||||
handle = handle.decode('utf-8')
|
||||
sigs[key][trans_type].append(handle)
|
||||
# now emit the signals
|
||||
self.undo_sigs(sigs, False)
|
||||
|
||||
if key != REFERENCE_KEY:
|
||||
self.undo_signals(trans_type, handle,
|
||||
db.emit, _SIGBASE[key], False)
|
||||
# Notify listeners
|
||||
if db.undo_callback:
|
||||
db.undo_callback(_("_Undo %s")
|
||||
% transaction.get_description())
|
||||
if self.db.undo_callback:
|
||||
self.db.undo_callback(_("_Undo %s")
|
||||
% transaction.get_description())
|
||||
|
||||
if db.redo_callback:
|
||||
if self.db.redo_callback:
|
||||
if self.redo_count > 1:
|
||||
new_transaction = self.redoq[-2]
|
||||
db.redo_callback(_("_Redo %s")
|
||||
% new_transaction.get_description())
|
||||
self.db.redo_callback(_("_Redo %s")
|
||||
% new_transaction.get_description())
|
||||
else:
|
||||
db.redo_callback(None)
|
||||
self.db.redo_callback(None)
|
||||
|
||||
if update_history and db.undo_history_callback:
|
||||
db.undo_history_callback()
|
||||
if update_history and self.db.undo_history_callback:
|
||||
self.db.undo_history_callback()
|
||||
return True
|
||||
|
||||
def undo_reference(self, data, handle, db_map):
|
||||
@ -337,19 +333,34 @@ class DbUndo:
|
||||
self.db._log_error()
|
||||
raise DbError(msg)
|
||||
|
||||
def undo_signals(self, trans_type, handle, emit, signal_root, reverse):
|
||||
def undo_sigs(self, sigs, undo):
|
||||
"""
|
||||
Helper method to undo/redo the changes made
|
||||
Helper method to undo/redo the signals for changes made
|
||||
We want to do deletes and adds first
|
||||
Note that if 'undo' we swap emits
|
||||
"""
|
||||
if ((not reverse) and trans_type == TXNADD) \
|
||||
or (reverse and trans_type == TXNDEL):
|
||||
typ = '-add'
|
||||
elif not reverse and trans_type == TXNDEL \
|
||||
or reverse and trans_type == TXNADD:
|
||||
typ = '-delete'
|
||||
else: # TXNUPD
|
||||
typ = '-update'
|
||||
emit(signal_root + typ, ([handle.decode('utf-8')],))
|
||||
for trans_type in [TXNDEL, TXNADD, TXNUPD]:
|
||||
for obj_type in range(11):
|
||||
handles = sigs[obj_type][trans_type]
|
||||
if handles:
|
||||
if not undo and trans_type == TXNDEL \
|
||||
or undo and trans_type == TXNADD:
|
||||
typ = '-delete'
|
||||
else:
|
||||
# don't update a handle if its been deleted, and note
|
||||
# that 'deleted' handles are in the 'add' list if we
|
||||
# are undoing
|
||||
handles = [handle for handle in handles
|
||||
if handle not in
|
||||
sigs[obj_type][TXNADD if undo else TXNDEL]]
|
||||
if ((not undo) and trans_type == TXNADD) \
|
||||
or (undo and trans_type == TXNDEL):
|
||||
typ = '-add'
|
||||
else: # TXNUPD
|
||||
typ = '-update'
|
||||
if handles:
|
||||
self.db.emit(KEY_TO_NAME_MAP[obj_type] + typ,
|
||||
(handles,))
|
||||
|
||||
undo_count = property(lambda self:len(self.undoq))
|
||||
redo_count = property(lambda self:len(self.redoq))
|
||||
|
@ -2034,11 +2034,11 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
self.txn = None
|
||||
self.env.log_flush()
|
||||
if not transaction.batch:
|
||||
emit = self.__emit
|
||||
for obj_type, obj_name in KEY_TO_NAME_MAP.items():
|
||||
emit(transaction, obj_type, TXNADD, obj_name, '-add')
|
||||
emit(transaction, obj_type, TXNUPD, obj_name, '-update')
|
||||
emit(transaction, obj_type, TXNDEL, obj_name, '-delete')
|
||||
# do deletes and adds first
|
||||
for trans_type in [TXNDEL, TXNADD, TXNUPD]:
|
||||
for obj_type in range(11):
|
||||
if obj_type != REFERENCE_KEY:
|
||||
self.__emit(transaction, obj_type, trans_type)
|
||||
self.transaction = None
|
||||
transaction.clear()
|
||||
self.undodb.commit(transaction, msg)
|
||||
@ -2051,21 +2051,23 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
hex(id(self)),
|
||||
transaction.get_description()))
|
||||
|
||||
def __emit(self, transaction, obj_type, trans_type, obj, suffix):
|
||||
def __emit(self, transaction, obj_type, trans_type):
|
||||
"""
|
||||
Define helper function to do the actual emits
|
||||
"""
|
||||
if (obj_type, trans_type) in transaction:
|
||||
if trans_type == TXNDEL:
|
||||
handles = [handle.decode('utf-8') for handle, data in
|
||||
transaction[(obj_type, trans_type)]]
|
||||
transaction[(obj_type, trans_type)]]
|
||||
else:
|
||||
handles = [handle.decode('utf-8') for handle, data in
|
||||
transaction[(obj_type, trans_type)]
|
||||
if (handle, None) not in transaction[(obj_type,
|
||||
TXNDEL)]]
|
||||
transaction[(obj_type, trans_type)]
|
||||
if (handle, None) not in transaction[(obj_type,
|
||||
TXNDEL)]]
|
||||
if handles:
|
||||
self.emit(obj + suffix, (handles, ))
|
||||
self.emit(KEY_TO_NAME_MAP[obj_type] +
|
||||
['-add', '-update', '-delete'][trans_type],
|
||||
(handles, ))
|
||||
|
||||
def transaction_abort(self, transaction):
|
||||
"""
|
||||
|
@ -308,21 +308,23 @@ class DBAPI(DbGeneric):
|
||||
self.dbapi.commit()
|
||||
if not txn.batch:
|
||||
# Now, emit signals:
|
||||
for (obj_type_val, txn_type_val) in list(txn):
|
||||
if obj_type_val == REFERENCE_KEY:
|
||||
continue
|
||||
if txn_type_val == TXNDEL:
|
||||
handles = [handle for (handle, data) in
|
||||
txn[(obj_type_val, txn_type_val)]]
|
||||
else:
|
||||
handles = [handle for (handle, data) in
|
||||
txn[(obj_type_val, txn_type_val)]
|
||||
if (handle, None)
|
||||
not in txn[(obj_type_val, TXNDEL)]]
|
||||
if handles:
|
||||
signal = KEY_TO_NAME_MAP[
|
||||
obj_type_val] + action[txn_type_val]
|
||||
self.emit(signal, (handles, ))
|
||||
# do deletes and adds first
|
||||
for trans_type in [TXNDEL, TXNADD, TXNUPD]:
|
||||
for obj_type in range(11):
|
||||
if obj_type != REFERENCE_KEY and \
|
||||
(obj_type, trans_type) in txn:
|
||||
if trans_type == TXNDEL:
|
||||
handles = [handle for (handle, data) in
|
||||
txn[(obj_type, trans_type)]]
|
||||
else:
|
||||
handles = [handle for (handle, data) in
|
||||
txn[(obj_type, trans_type)]
|
||||
if (handle, None)
|
||||
not in txn[(obj_type, TXNDEL)]]
|
||||
if handles:
|
||||
signal = KEY_TO_NAME_MAP[
|
||||
obj_type] + action[trans_type]
|
||||
self.emit(signal, (handles, ))
|
||||
self.transaction = None
|
||||
msg = txn.get_description()
|
||||
self.undodb.commit(txn, msg)
|
||||
|
Loading…
Reference in New Issue
Block a user