Skip to content

Commit

Permalink
Merge fb62385 into 14aa616
Browse files Browse the repository at this point in the history
  • Loading branch information
jimfulton committed Nov 17, 2016
2 parents 14aa616 + fb62385 commit f3118e5
Show file tree
Hide file tree
Showing 32 changed files with 396 additions and 171 deletions.
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
Change History
================

- ZODB now translates transaction meta data, ``user`` and
``description`` from text to bytes before passing them to storages,
and converts them back to text when retrieving them from storages in
the ``history``, ``undoLog`` and ``undoInfo`` methods.

The ``IDatabase`` interface was updated to reflect that ``history``,
``undoLog`` and ``undoInfo`` are available on database objects.
(They were always available, but not documented in the interface.)

5.0.1 (2016-11-17)
==================

Expand Down
9 changes: 5 additions & 4 deletions doc/guide/transactions-and-threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ statement. Transaction managers are context managers, so we can use
them with the ``with`` statement directly::

with my_transaction_manager as trans:
trans.note("incrementing x")
trans.note(u"incrementing x")
conn.root.x += 1

.. -> src
Expand Down Expand Up @@ -181,14 +181,15 @@ So, for example, if we wanted to set a transaction note::


with db.transaction() as conn2:
conn2.transaction_manager.get().note("incrementing x again")
conn2.transaction_manager.get().note(u"incrementing x again")
conn2.root.x += 1

.. -> src
>>> exec(src)
>>> str(db.history(conn.root()._p_oid)[0]['description'])
'incrementing x again'
>>> (db.history(conn.root()._p_oid)[0]['description'] ==
... u'incrementing x again')
True
Here, we used the
:meth:`~transaction.interfaces.ITransactionManager.get` method to get
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def read_file(*path):
'persistent >= 4.2.0',
'BTrees >= 4.2.0',
'ZConfig',
'transaction >= 1.6.1',
'transaction >= 2.0.3',
'six',
'zc.lockfile',
'zope.interface',
Expand Down
2 changes: 1 addition & 1 deletion src/ZODB/BaseStorage.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def tpc_begin(self, transaction, tid=None, status=' '):

user = transaction.user
desc = transaction.description
ext = transaction._extension
ext = transaction.extension
if ext:
ext = dumps(ext, _protocol)
else:
Expand Down
51 changes: 50 additions & 1 deletion src/ZODB/Connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from .mvccadapter import HistoricalStorageAdapter

from . import valuedoc
from . import _compat

global_reset_counter = 0

Expand Down Expand Up @@ -458,15 +459,22 @@ def _tpc_cleanup(self):
def tpc_begin(self, transaction):
"""Begin commit of a transaction, starting the two-phase commit."""
self._modified = []
meta_data = TransactionMetaData(
transaction.user,
transaction.description,
transaction.extension)
transaction.set_data(self, meta_data)

# _creating is a list of oids of new objects, which is used to
# remove them from the cache if a transaction aborts.
self._creating.clear()
self._normal_storage.tpc_begin(transaction)
self._normal_storage.tpc_begin(meta_data)

def commit(self, transaction):
"""Commit changes to an object"""

transaction = transaction.data(self)

if self._savepoint_storage is not None:

# We first checkpoint the current changes to the savepoint
Expand Down Expand Up @@ -611,6 +619,8 @@ def _store_objects(self, writer, transaction):
obj._p_serial = s

def tpc_abort(self, transaction):
transaction = transaction.data(self)

if self._import:
self._import = None

Expand Down Expand Up @@ -667,6 +677,9 @@ def tpc_vote(self, transaction):
vote = self._storage.tpc_vote
except AttributeError:
return

transaction = transaction.data(self)

try:
s = vote(transaction)
except ReadConflictError as v:
Expand All @@ -683,6 +696,8 @@ def tpc_vote(self, transaction):
def tpc_finish(self, transaction):
"""Indicate confirmation that the transaction is done.
"""
transaction = transaction.data(self)

serial = self._storage.tpc_finish(transaction)
assert type(serial) is bytes, repr(serial)
for oid_iterator in self._modified, self._creating:
Expand Down Expand Up @@ -1270,3 +1285,37 @@ def __repr__(self):
large-record-size option in a configuration file) to specify a larger
size.
"""

class TransactionMetaData(object):

def __init__(self, user=u'', description=u'', extension=b''):
if not isinstance(user, bytes):
user = user.encode('utf-8')
self.user = user

if not isinstance(description, bytes):
description = description.encode('utf-8')
self.description = description

if not isinstance(extension, dict):
extension = _compat.loads(extension) if extension else {}
self.extension = extension

def note(self, text): # for tests
text = text.strip()
if not isinstance(text, bytes):
text = text.encode('utf-8')
if self.description:
self.description = self.description.strip() + b' ' + text
else:
self.description = text

@property
def _extension(self):
warnings.warn("_extension is deprecated, use extension",
DeprecationWarning)
return self.extension

@_extension.setter
def _extension(self, v):
self.extension = v
30 changes: 24 additions & 6 deletions src/ZODB/DB.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from ZODB.broken import find_global
from ZODB.utils import z64
from ZODB.Connection import Connection
from ZODB.Connection import Connection, TransactionMetaData
from ZODB._compat import Pickler, _protocol, BytesIO
import ZODB.serialize

Expand Down Expand Up @@ -469,7 +469,7 @@ def __init__(self,
self.large_record_size = large_record_size

# Make sure we have a root:
with self.transaction('initial database creation') as conn:
with self.transaction(u'initial database creation') as conn:
try:
conn.get(z64)
except KeyError:
Expand Down Expand Up @@ -901,7 +901,7 @@ def history(self, oid, size=1):
See :meth:`ZODB.interfaces.IStorage.history`.
"""
return self.storage.history(oid, size)
return _text_transaction_info(self.storage.history(oid, size))

def supportsUndo(self):
"""Return whether the database supports undo.
Expand All @@ -920,7 +920,7 @@ def undoLog(self, *args, **kw):

if not self.supportsUndo():
return ()
return self.storage.undoLog(*args, **kw)
return _text_transaction_info(self.storage.undoLog(*args, **kw))

def undoInfo(self, *args, **kw):
"""Return a sequence of descriptions for transactions.
Expand All @@ -929,7 +929,7 @@ def undoInfo(self, *args, **kw):
"""
if not self.supportsUndo():
return ()
return self.storage.undoInfo(*args, **kw)
return _text_transaction_info(self.storage.undoInfo(*args, **kw))

def undoMultiple(self, ids, txn=None):
"""Undo multiple transactions identified by ids.
Expand Down Expand Up @@ -1037,19 +1037,28 @@ def abort(self, transaction):
pass

def tpc_begin(self, transaction):
self._storage.tpc_begin(transaction)
tdata = TransactionMetaData(
transaction.user,
transaction.description,
transaction.extension)
transaction.set_data(self, tdata)
self._storage.tpc_begin(tdata)

def commit(self, transaction):
transaction = transaction.data(self)
for tid in self._tids:
self._storage.undo(tid, transaction)

def tpc_vote(self, transaction):
transaction = transaction.data(self)
self._storage.tpc_vote(transaction)

def tpc_finish(self, transaction):
transaction = transaction.data(self)
self._storage.tpc_finish(transaction)

def tpc_abort(self, transaction):
transaction = transaction.data(self)
self._storage.tpc_abort(transaction)

def sortKey(self):
Expand All @@ -1064,3 +1073,12 @@ def connection(*args, **kw):
managing a separate database object.
"""
return DB(*args, **kw).open_then_close_db_when_connection_closes()

_transaction_meta_data_text_variables = 'user_name', 'description'
def _text_transaction_info(info):
for d in info:
for name in _transaction_meta_data_text_variables:
if name in d:
d[name] = d[name].decode('utf-8')

return info
5 changes: 3 additions & 2 deletions src/ZODB/FileStorage/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import ZODB.blob
import ZODB.FileStorage
import ZODB.tests.util
from ZODB.Connection import TransactionMetaData
from zope.testing import renormalizing

checker = renormalizing.RENormalizing([
Expand Down Expand Up @@ -124,7 +125,7 @@ def pack_with_repeated_blob_records():
Now, create a transaction with multiple saves:
>>> trans = tm.begin()
>>> trans = TransactionMetaData()
>>> fs.tpc_begin(trans)
>>> with open('ablob', 'w') as file:
... _ = file.write('some data')
Expand All @@ -151,7 +152,7 @@ def _save_index():
>>> import ZODB.utils
>>> fs = ZODB.FileStorage.FileStorage('data.fs')
>>> t = transaction.begin()
>>> t = TransactionMetaData()
>>> fs.tpc_begin(t)
>>> oid = 0
>>> for i in range(5000):
Expand Down
2 changes: 1 addition & 1 deletion src/ZODB/MappingStorage.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def __init__(self, tid, transaction, data):
self.tid = tid
self.user = transaction.user
self.description = transaction.description
extension = transaction._extension
extension = transaction.extension
self.extension = extension
self.data = data

Expand Down
Loading

0 comments on commit f3118e5

Please sign in to comment.