Commit 308932a3 authored by Barry Warsaw's avatar Barry Warsaw

transactionalUndo(): When we're undoing the last revision of an

object, don't rely on its prevrevid pointing to the previous metadata
record.  That's because if the last revision was an undo record, it
will have a prevrevid pointing to the revision previous to the undone
one (phew! :).  Instead, use a cursor to find the previous metadata
record to restore.

supportsTransactionalUndo(): Added.
parent 2bdeec1b
...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
# $Revision: 1.14 $ # $Revision: 1.15 $
__version__ = '0.1' __version__ = '0.1'
import struct import struct
...@@ -631,16 +631,34 @@ class Full(BerkeleyBase): ...@@ -631,16 +631,34 @@ class Full(BerkeleyBase):
# the state changes couldn't be resolved. # the state changes couldn't be resolved.
revid = self._serials[oid] revid = self._serials[oid]
if revid == tid: if revid == tid:
# We can always undo the last transaction
vid, nvrevid, lrevid, prevrevid = struct.unpack( vid, nvrevid, lrevid, prevrevid = struct.unpack(
'>8s8s8s8s', self._metadata[oid+tid]) '>8s8s8s8s', self._metadata[oid+tid])
if prevrevid == ZERO: # We can always undo the last transaction. The prevrevid
# We're undoing the object's creation. The only thing # pointer doesn't necessarily point to the previous
# to undo from there is the zombification of the # transaction, if the revision we're undoing was itself an
# object, i.e. restore the current revision. # undo. Use a cursor to find the previous revision of
# this object.
mdc = self._metadata.cursor()
try:
mdc.set(oid+revid)
mrec = mdc.prev()
# If we're undoing the first record, either for the
# whole system or for this object, just write a
# zombification record
if not mrec or mrec[0][:8] <> oid:
newrevs.append((oid, vid+nvrevid+DNE+revid)) newrevs.append((oid, vid+nvrevid+DNE+revid))
else: continue
newrevs.append((oid, self._metadata[oid+prevrevid])) # BAW: If the revid of this object record is the same
# as the revid we're being asked to undo, then I think
# we have a problem (since the storage invariant is
# that it doesn't retain metadata records for multiple
# modifications of the object in the same txn).
if mrec[0][8:] == revid:
raise StorageSystemError
# All is good, so just restore this metadata record
newrevs.append((oid, mrec[1]))
finally:
mdc.close()
else: else:
# We need to compare the lrevid (pickle pointers) of the # We need to compare the lrevid (pickle pointers) of the
# transaction previous to the current one, and the # transaction previous to the current one, and the
...@@ -934,6 +952,9 @@ class Full(BerkeleyBase): ...@@ -934,6 +952,9 @@ class Full(BerkeleyBase):
self._lock_release() self._lock_release()
# Other interface assertions # Other interface assertions
def supportsTransactionalUndo(self):
return 1
def supportsUndo(self): def supportsUndo(self):
return 1 return 1
......
...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support ...@@ -4,7 +4,7 @@ See Minimal.py for an implementation of Berkeley storage that does not support
undo or versioning. undo or versioning.
""" """
# $Revision: 1.14 $ # $Revision: 1.15 $
__version__ = '0.1' __version__ = '0.1'
import struct import struct
...@@ -631,16 +631,34 @@ class Full(BerkeleyBase): ...@@ -631,16 +631,34 @@ class Full(BerkeleyBase):
# the state changes couldn't be resolved. # the state changes couldn't be resolved.
revid = self._serials[oid] revid = self._serials[oid]
if revid == tid: if revid == tid:
# We can always undo the last transaction
vid, nvrevid, lrevid, prevrevid = struct.unpack( vid, nvrevid, lrevid, prevrevid = struct.unpack(
'>8s8s8s8s', self._metadata[oid+tid]) '>8s8s8s8s', self._metadata[oid+tid])
if prevrevid == ZERO: # We can always undo the last transaction. The prevrevid
# We're undoing the object's creation. The only thing # pointer doesn't necessarily point to the previous
# to undo from there is the zombification of the # transaction, if the revision we're undoing was itself an
# object, i.e. restore the current revision. # undo. Use a cursor to find the previous revision of
# this object.
mdc = self._metadata.cursor()
try:
mdc.set(oid+revid)
mrec = mdc.prev()
# If we're undoing the first record, either for the
# whole system or for this object, just write a
# zombification record
if not mrec or mrec[0][:8] <> oid:
newrevs.append((oid, vid+nvrevid+DNE+revid)) newrevs.append((oid, vid+nvrevid+DNE+revid))
else: continue
newrevs.append((oid, self._metadata[oid+prevrevid])) # BAW: If the revid of this object record is the same
# as the revid we're being asked to undo, then I think
# we have a problem (since the storage invariant is
# that it doesn't retain metadata records for multiple
# modifications of the object in the same txn).
if mrec[0][8:] == revid:
raise StorageSystemError
# All is good, so just restore this metadata record
newrevs.append((oid, mrec[1]))
finally:
mdc.close()
else: else:
# We need to compare the lrevid (pickle pointers) of the # We need to compare the lrevid (pickle pointers) of the
# transaction previous to the current one, and the # transaction previous to the current one, and the
...@@ -934,6 +952,9 @@ class Full(BerkeleyBase): ...@@ -934,6 +952,9 @@ class Full(BerkeleyBase):
self._lock_release() self._lock_release()
# Other interface assertions # Other interface assertions
def supportsTransactionalUndo(self):
return 1
def supportsUndo(self): def supportsUndo(self):
return 1 return 1
......
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