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