diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c75f83499d23a83659089dbc2c8b805fc2bc9e59..04dadbfb0c0d3221527d396852c2e1f1581aec21 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -8,6 +8,9 @@ Bugs Fixed ---------- +- DemoStorage.loadBefore sometimes returned invalid data which + would trigger AssertionErrors in ZODB.Connection. + - History support was broken when using stprages that work with ZODB 3.8 and 3.9. diff --git a/src/ZODB/DemoStorage.py b/src/ZODB/DemoStorage.py index 40225f8aa3b9837719f6e4ff85d6d07f43d48153..5332eaefe2132d79e7f8048c568eca02c3f63af6 100644 --- a/src/ZODB/DemoStorage.py +++ b/src/ZODB/DemoStorage.py @@ -153,11 +153,18 @@ class DemoStorage(object): # The oid *was* in the changes, but there aren't any # earlier records. Maybe there are in the base. try: - return self.base.loadBefore(oid, tid) + result = self.base.loadBefore(oid, tid) except ZODB.POSException.POSKeyError: # The oid isn't in the base, so None will be the right result pass - + else: + if result and not result[-1]: + end_tid = None + t = self.changes.load(oid) + while t: + end_tid = t[1] + t = self.changes.loadBefore(oid, end_tid) + result = result[:2] + (end_tid,) return result def loadBlob(self, oid, serial): diff --git a/src/ZODB/tests/testDemoStorage.py b/src/ZODB/tests/testDemoStorage.py index 857aa467062c5c1f4f92dd4d00d5d9c913a2c1f8..373bf35e774fd2e88783276b51f1a5e630605bae 100644 --- a/src/ZODB/tests/testDemoStorage.py +++ b/src/ZODB/tests/testDemoStorage.py @@ -171,6 +171,53 @@ def blob_pos_key_error_with_non_blob_base(): """ +def load_before_base_storage_current(): + """ + Here we'll exercise that DemoStorage's loadBefore method works + properly when deferring to a record that is current in the + base storage. + + >>> import transaction + >>> import ZODB.DB + >>> import ZODB.DemoStorage + >>> import ZODB.MappingStorage + >>> import ZODB.utils + + >>> base = ZODB.MappingStorage.MappingStorage() + >>> basedb = ZODB.DB(base) + >>> conn = basedb.open() + >>> conn.root()['foo'] = 'bar' + >>> transaction.commit() + >>> conn.close() + >>> storage = ZODB.DemoStorage.DemoStorage(base=base) + >>> db = ZODB.DB(storage) + >>> conn = db.open() + >>> conn.root()['foo'] = 'baz' + >>> transaction.commit() + + >>> oid = ZODB.utils.z64 + >>> base_current = storage.base.load(oid) + >>> tid = ZODB.utils.p64(ZODB.utils.u64(base_current[1]) + 1) + >>> base_record = storage.base.loadBefore(oid, tid) + >>> base_record[-1] is None + True + >>> base_current == base_record[:2] + True + + >>> t = storage.loadBefore(oid, tid) + + The data and tid are the values from the base storage, but the + next tid is from changes. + + >>> t[:2] == base_record[:2] + True + >>> t[-1] == storage.changes.load(oid)[1] + True + + >>> conn.close() + >>> db.close() + >>> base.close() + """ def test_suite(): suite = unittest.TestSuite((