[Zodb-checkins] CVS: Packages/bsddb3Storage - Full.py:1.11

barry@digicool.com barry@digicool.com
Fri, 6 Apr 2001 14:44:06 -0400 (EDT)


Update of /cvs-repository/Packages/bsddb3Storage
In directory korak:/tmp/cvs-serv5930

Modified Files:
	Full.py 
Log Message:
transactionalUndo(): A new form of undo that is non-destructive,
i.e. it undoes by writing new records.  This means undone transactions
can be redone.  It also has lots of other benefits.



--- Updated File Full.py in package Packages/bsddb3Storage --
--- Full.py	2001/04/05 03:34:28	1.10
+++ Full.py	2001/04/06 18:44:05	1.11
@@ -607,8 +607,8 @@
         return self._serial
 
     def transactionalUndo(self, tid, transaction):
-        # FIXME: what if we undo an abortVersion or commitVersion, don't we
-        # need to re-populate the currentVersions table?
+        global zero
+
         if transaction is not self._transaction:
             raise POSException.StorageTransactionError(self, transaction)
 
@@ -616,7 +616,7 @@
         c = None
         self._lock_acquire()
         try:
-            # First, make sure the transaction isn't protected by a pack
+            # First, make sure the transaction isn't protected by a pack.
             status = self._txnMetadata[tid][1]
             if status == PROTECTED_TRANSACTION:
                 raise POSException.UndoError, 'Transaction cannot be undone'
@@ -625,42 +625,53 @@
             c = self._txnoids.cursor()
             rec = c.set(tid)
             while rec:
-                oid = rec[1]
+                oid = rec[1]                      # ignore the key
+                rec = c.next_dup()
                 # In order to be able to undo this transaction, we must be
                 # undoing either the current revision of the object, or we
                 # must be restoring the exact same pickle (identity compared)
                 # that would be restored if we were undoing the current
                 # revision.
+                #
+                # Note that we could do pickle equivalence comparisions
+                # instead.  That would be "temporaly clean" in that we'd still
+                # be restoring the same state.  We decided not to do this for
+                # now.  Eventually, when we have application level conflict
+                # resolution, we can ask the object if it can resolve the
+                # state change, and then we'd reject the undo only if any of
+                # the state changes couldn't be resolved.
                 revid = self._serials[oid]
                 if revid == tid:
+                    # We can always undo the last transaction
                     prevrevid = self._metadata[oid+tid][24:]
+                    if prevrevid == zero:
+                        raise POSException.UndoError, 'Nothing to undo'
                     newrevs.append((oid, self._metadata[oid+prevrevid]))
                 else:
-                    # Compare the lrevid (pickle pointers) for the current
-                    # revision of the object and the revision previous to the
-                    # one we're undoing.
-                    lrevid = self._metadata[oid+revid][16:24]
-                    # When we undo this transaction, the previous record will
-                    # become the current record.
-                    prevrevid = self._metadata[oid+tid][24:]
-                    # And here's the pickle pointer for that potentially
-                    # soon-to-be current record
-                    prevrec = self._metadata[oid+prevrevid]
-                    if lrevid <> prevrec[16:24]:
-                        # They aren't the same, so we cannot undo this txn
+                    # We need to compare the lrevid (pickle pointers) of the
+                    # transaction previous to the current one, and the
+                    # transaction previous to the one we want to undo.  If
+                    # their lrevids are the same, it's undoable.
+                    target_prevrevid = self._metadata[oid+tid][24:]
+                    if target_prevrevid == zero:
+                        raise POSException.UndoError, 'Nothing to undo'
+                    target_metadata  = self._metadata[oid+target_prevrevid]
+                    target_lrevid    = target_metadata[16:24]
+                    last_prevrevid = self._metadata[oid+revid][24:]
+                    last_lrevid    = self._metadata[oid+last_prevrevid][16:24]
+                    # BAW: Here's where application level conflict resolution,
+                    # or pickle equivalence testing would go.
+                    if target_lrevid <> last_lrevid:
                         raise POSException.UndoError, 'Cannot undo transaction'
-                    newrevs.append((oid, prevrec))
-                # Check the next txnoid record
-                rec = c.next()
-            # Okay, we've checked all the oids affected by the transaction
+                    # So far so good
+                    newrevs.append((oid, target_metadata))
+            # Okay, we've checked all the objects affected by the transaction
             # we're about to undo, and everything looks good.  So now we'll
             # write to the log the new object records we intend to commit.
-            c.close()
-            c = None
             oids = []
-            for oid, rec in newrevs:
+            for oid, metadata in newrevs:
                 vid, nvrevid, lrevid, prevrevid = struct.unpack(
-                    '>8s8s8s8s', rec)
+                    '>8s8s8s8s', metadata)
                 self._commitlog.write_moved_object(oid, vid, nvrevid, lrevid,
                                                    prevrevid)
                 oids.append(oid)