Commit 6c0b5609 authored by Jim Fulton's avatar Jim Fulton

Merge remote-tracking branch 'origin/master' into no-more-load

parents c1d79d72 b7ea4e6f
...@@ -26,6 +26,11 @@ ...@@ -26,6 +26,11 @@
transaction. transaction.
https://github.com/zopefoundation/ZODB/pull/52 https://github.com/zopefoundation/ZODB/pull/52
- DemoStorage: add support for conflict resolution and fix history()
https://github.com/zopefoundation/ZODB/pull/58
- Fixed: FileStorage loadBefore didn't handle deleted/undone data correctly.
4.2.0 (2015-06-02) 4.2.0 (2015-06-02)
================== ==================
......
...@@ -23,23 +23,8 @@ interface, rich transaction support, and undo. ...@@ -23,23 +23,8 @@ interface, rich transaction support, and undo.
version = "4.3.0.dev0" version = "4.3.0.dev0"
import os import os
import sys
from setuptools import setup, find_packages from setuptools import setup, find_packages
if sys.version_info < (2, 6):
print("This version of ZODB requires Python 2.6 or higher")
sys.exit(0)
if (3,) < sys.version_info < (3, 2):
print("This version of ZODB requires Python 3.2 or higher")
sys.exit(0)
# The (non-obvious!) choices for the Trove Development Status line:
# Development Status :: 5 - Production/Stable
# Development Status :: 4 - Beta
# Development Status :: 3 - Alpha
classifiers = """\ classifiers = """\
Development Status :: 4 - Beta Development Status :: 4 - Beta
Intended Audience :: Developers Intended Audience :: Developers
......
...@@ -313,4 +313,9 @@ class ConflictResolvingStorage(object): ...@@ -313,4 +313,9 @@ class ConflictResolvingStorage(object):
def registerDB(self, wrapper): def registerDB(self, wrapper):
self._crs_untransform_record_data = wrapper.untransform_record_data self._crs_untransform_record_data = wrapper.untransform_record_data
self._crs_transform_record_data = wrapper.transform_record_data self._crs_transform_record_data = wrapper.transform_record_data
super(ConflictResolvingStorage, self).registerDB(wrapper) try:
m = super(ConflictResolvingStorage, self).registerDB
except AttributeError:
pass
else:
m(wrapper)
...@@ -31,12 +31,13 @@ import ZODB.MappingStorage ...@@ -31,12 +31,13 @@ import ZODB.MappingStorage
import ZODB.POSException import ZODB.POSException
import ZODB.utils import ZODB.utils
import zope.interface import zope.interface
from .ConflictResolution import ConflictResolvingStorage, ResolvedSerial
@zope.interface.implementer( @zope.interface.implementer(
ZODB.interfaces.IStorage, ZODB.interfaces.IStorage,
ZODB.interfaces.IStorageIteration, ZODB.interfaces.IStorageIteration,
) )
class DemoStorage(object): class DemoStorage(ConflictResolvingStorage):
def __init__(self, name=None, base=None, changes=None, def __init__(self, name=None, base=None, changes=None,
...@@ -112,7 +113,7 @@ class DemoStorage(object): ...@@ -112,7 +113,7 @@ class DemoStorage(object):
def _copy_methods_from_changes(self, changes): def _copy_methods_from_changes(self, changes):
for meth in ( for meth in (
'_lock_acquire', '_lock_release', '_lock_acquire', '_lock_release',
'getSize', 'history', 'isReadOnly', 'registerDB', 'getSize', 'isReadOnly',
'sortKey', 'tpc_transaction', 'tpc_vote', 'sortKey', 'tpc_transaction', 'tpc_vote',
): ):
setattr(self, meth, getattr(changes, meth)) setattr(self, meth, getattr(changes, meth))
...@@ -137,6 +138,20 @@ class DemoStorage(object): ...@@ -137,6 +138,20 @@ class DemoStorage(object):
except ZODB.POSException.POSKeyError: except ZODB.POSException.POSKeyError:
return self.base.getTid(oid) return self.base.getTid(oid)
def history(self, oid, size=1):
try:
r = self.changes.history(oid, size)
except ZODB.POSException.POSKeyError:
r = []
size -= len(r)
if size:
try:
r += self.base.history(oid, size)
except ZODB.POSException.POSKeyError:
if not r:
raise
return r
def iterator(self, start=None, end=None): def iterator(self, start=None, end=None):
for t in self.base.iterator(start, end): for t in self.base.iterator(start, end):
yield t yield t
...@@ -281,8 +296,9 @@ class DemoStorage(object): ...@@ -281,8 +296,9 @@ class DemoStorage(object):
old = serial old = serial
if old != serial: if old != serial:
raise ZODB.POSException.ConflictError( rdata = self.tryToResolveConflict(oid, old, serial, data)
oid=oid, serials=(old, serial)) # XXX untested branch self.changes.store(oid, old, rdata, '', transaction)
return ResolvedSerial
return self.changes.store(oid, serial, data, '', transaction) return self.changes.store(oid, serial, data, '', transaction)
......
...@@ -490,11 +490,13 @@ class FileStorage( ...@@ -490,11 +490,13 @@ class FileStorage(
if not pos: if not pos:
return None return None
if h.back: if h.plen:
return _file.read(h.plen), h.tid, end_tid
elif h.back:
data, _, _, _ = self._loadBack_impl(oid, h.back, _file=_file) data, _, _, _ = self._loadBack_impl(oid, h.back, _file=_file)
return data, h.tid, end_tid return data, h.tid, end_tid
else: else:
return _file.read(h.plen), h.tid, end_tid raise POSKeyError(oid)
def store(self, oid, oldserial, data, version, transaction): def store(self, oid, oldserial, data, version, transaction):
if self._is_read_only: if self._is_read_only:
...@@ -1645,7 +1647,7 @@ def read_index(file, name, index, tindex, stop=b'\377'*8, ...@@ -1645,7 +1647,7 @@ def read_index(file, name, index, tindex, stop=b'\377'*8,
maxoid = index.maxKey() maxoid = index.maxKey()
except ValueError: except ValueError:
# The index is empty. # The index is empty.
maxoid == z64 pass # maxoid is already equal to z64
return pos, maxoid, ltid return pos, maxoid, ltid
......
...@@ -105,7 +105,7 @@ class MappingStorage(object): ...@@ -105,7 +105,7 @@ class MappingStorage(object):
tids.reverse() tids.reverse()
return [ return [
dict( dict(
time = ZODB.TimeStamp.TimeStamp(tid), time = ZODB.TimeStamp.TimeStamp(tid).timeTime(),
tid = tid, tid = tid,
serial = tid, serial = tid,
user_name = self._transactions[tid].user, user_name = self._transactions[tid].user,
......
...@@ -17,45 +17,31 @@ Any storage that supports the history() method should be able to pass ...@@ -17,45 +17,31 @@ Any storage that supports the history() method should be able to pass
all these tests. all these tests.
""" """
from time import time
from ZODB.tests.MinPO import MinPO from ZODB.tests.MinPO import MinPO
class HistoryStorage: class HistoryStorage:
def checkSimpleHistory(self): def checkSimpleHistory(self):
eq = self.assertEqual self._checkHistory((11, 12, 13))
def _checkHistory(self, data):
start = time()
# Store a couple of revisions of the object # Store a couple of revisions of the object
oid = self._storage.new_oid() oid = self._storage.new_oid()
self.assertRaises(KeyError,self._storage.history,oid) self.assertRaises(KeyError,self._storage.history,oid)
revid1 = self._dostore(oid, data=MinPO(11)) revids = [None]
revid2 = self._dostore(oid, revid=revid1, data=MinPO(12)) for data in data:
revid3 = self._dostore(oid, revid=revid2, data=MinPO(13)) revids.append(self._dostore(oid, revids[-1], MinPO(data)))
revids.reverse()
del revids[-1]
# Now get various snapshots of the object's history # Now get various snapshots of the object's history
h = self._storage.history(oid, size=1) for i in range(1, 1 + len(revids)):
eq(len(h), 1) h = self._storage.history(oid, size=i)
d = h[0] self.assertEqual([d['tid'] for d in h], revids[:i])
eq(d['tid'], revid3) # Check results are sorted by timestamp, in descending order.
# Try to get 2 historical revisions a = time()
h = self._storage.history(oid, size=2) for d in h:
eq(len(h), 2) b = a
d = h[0] a = d['time']
eq(d['tid'], revid3) self.assertLess(a, b)
d = h[1] self.assertLess(start, a)
eq(d['tid'], revid2)
# Try to get all 3 historical revisions
h = self._storage.history(oid, size=3)
eq(len(h), 3)
d = h[0]
eq(d['tid'], revid3)
d = h[1]
eq(d['tid'], revid2)
d = h[2]
eq(d['tid'], revid1)
# There should be no more than 3 revisions
h = self._storage.history(oid, size=4)
eq(len(h), 3)
d = h[0]
eq(d['tid'], revid3)
d = h[1]
eq(d['tid'], revid2)
d = h[2]
eq(d['tid'], revid1)
...@@ -179,6 +179,10 @@ class TransactionalUndoStorage: ...@@ -179,6 +179,10 @@ class TransactionalUndoStorage:
info = self._storage.undoInfo() info = self._storage.undoInfo()
self._undo(info[2]['id'], [oid]) self._undo(info[2]['id'], [oid])
self.assertRaises(KeyError, self._storage.load, oid, '') self.assertRaises(KeyError, self._storage.load, oid, '')
# Loading current data via loadBefore should raise a POSKeyError too:
self.assertRaises(KeyError, self._storage.loadBefore, oid,
b'\x7f\xff\xff\xff\xff\xff\xff\xff')
self._iterate() self._iterate()
def checkUndoCreationBranch2(self): def checkUndoCreationBranch2(self):
...@@ -286,10 +290,9 @@ class TransactionalUndoStorage: ...@@ -286,10 +290,9 @@ class TransactionalUndoStorage:
t = Transaction() t = Transaction()
oids = self._begin_undos_vote(t, tid, tid1) oids = self._begin_undos_vote(t, tid, tid1)
self._storage.tpc_finish(t) self._storage.tpc_finish(t)
# We get the finalization stuff called an extra time: # We may get the finalization stuff called an extra time,
eq(len(oids), 4) # depending on the implementation.
unless(oid1 in oids) self.assertEqual(set(oids), set((oid1, oid2)))
unless(oid2 in oids)
data, revid1 = self._storage.load(oid1, '') data, revid1 = self._storage.load(oid1, '')
eq(zodb_unpickle(data), MinPO(30)) eq(zodb_unpickle(data), MinPO(30))
data, revid2 = self._storage.load(oid2, '') data, revid2 = self._storage.load(oid2, '')
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
from ZODB.DB import DB from ZODB.DB import DB
from ZODB.tests import ( from ZODB.tests import (
BasicStorage, BasicStorage,
ConflictResolution,
HistoryStorage, HistoryStorage,
IteratorStorage, IteratorStorage,
MTStorage, MTStorage,
...@@ -42,7 +43,7 @@ from zope.testing import renormalizing ...@@ -42,7 +43,7 @@ from zope.testing import renormalizing
class DemoStorageTests( class DemoStorageTests(
StorageTestBase.StorageTestBase, StorageTestBase.StorageTestBase,
BasicStorage.BasicStorage, BasicStorage.BasicStorage,
ConflictResolution.ConflictResolvingStorage,
HistoryStorage.HistoryStorage, HistoryStorage.HistoryStorage,
IteratorStorage.ExtendedIteratorStorage, IteratorStorage.ExtendedIteratorStorage,
IteratorStorage.IteratorStorage, IteratorStorage.IteratorStorage,
...@@ -86,6 +87,23 @@ class DemoStorageTests( ...@@ -86,6 +87,23 @@ class DemoStorageTests(
pass # we don't support undo yet pass # we don't support undo yet
checkUndoZombie = checkLoadBeforeUndo checkUndoZombie = checkLoadBeforeUndo
def checkBaseHistory(self):
def base_only():
yield 11
yield 12
yield 13
self._storage = self._storage.push()
self._checkHistory(base_only())
self._storage = self._storage.pop()
def base_and_changes():
yield 11
yield 12
self._storage = self._storage.push()
yield 13
yield 14
self._checkHistory(base_and_changes())
self._storage = self._storage.pop()
class DemoStorageHexTests(DemoStorageTests): class DemoStorageHexTests(DemoStorageTests):
...@@ -144,11 +162,11 @@ def testSomeDelegation(): ...@@ -144,11 +162,11 @@ def testSomeDelegation():
>>> class S: >>> class S:
... def __init__(self, name): ... def __init__(self, name):
... self.name = name ... self.name = name
... def registerDB(self, db): ... def getSize(self):
... six.print_(self.name, db) ... six.print_(self.name, 'size')
... def close(self): ... def close(self):
... six.print_(self.name, 'closed') ... six.print_(self.name, 'closed')
... sortKey = getSize = __len__ = history = getTid = None ... sortKey = __len__ = getTid = None
... tpc_finish = tpc_vote = tpc_transaction = None ... tpc_finish = tpc_vote = tpc_transaction = None
... _lock_acquire = _lock_release = lambda self: None ... _lock_acquire = _lock_release = lambda self: None
... getName = lambda self: 'S' ... getName = lambda self: 'S'
...@@ -165,8 +183,8 @@ def testSomeDelegation(): ...@@ -165,8 +183,8 @@ def testSomeDelegation():
>>> from ZODB.DemoStorage import DemoStorage >>> from ZODB.DemoStorage import DemoStorage
>>> storage = DemoStorage(base=S(1), changes=S(2)) >>> storage = DemoStorage(base=S(1), changes=S(2))
>>> storage.registerDB(1) >>> storage.getSize()
2 1 2 size
>>> storage.close() >>> storage.close()
1 closed 1 closed
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment