[Zodb-checkins] SVN: ZODB/branches/jim-storage-api-cleanup/src/Z checkpointing

Jim Fulton jim at zope.com
Tue Apr 24 19:35:32 EDT 2007


Log message for revision 74732:
  checkpointing

Changed:
  U   ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py
  A   ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py
  U   ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py

-=-
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -266,8 +266,7 @@
         self._pickler = None
 
         self._info = {'length': 0, 'size': 0, 'name': 'ZEO Client',
-                      'supportsUndo':0, 'supportsVersions': 0,
-                      'supportsTransactionalUndo': 0}
+                      'supportsUndo':0, 'supportsVersions': 0}
 
         self._tbuf = self.TransactionBufferClass()
         self._db = None
@@ -670,10 +669,6 @@
         """Storage API: return whether we support versions."""
         return self._info['supportsVersions']
 
-    def supportsTransactionalUndo(self):
-        """Storage API: return whether we support transactional undo."""
-        return self._info['supportsTransactionalUndo']
-
     def isReadOnly(self):
         """Storage API: return whether we are in read-only mode."""
         if self._is_read_only:
@@ -732,15 +727,15 @@
         return self._server.history(oid, version, length)
 
     def record_iternext(self, next=None):
-        """Storage API: get the mext database record.
+        """Storage API: get the next database record.
 
         This is part of the conversion-support API.
         """
         return self._server.record_iternext(next)
 
-    def getSerial(self, oid):
+    def getTid(self, oid):
         """Storage API: return current serial number for oid."""
-        return self._server.getSerial(oid)
+        return self._server.getTid(oid)
 
     def loadSerial(self, oid, serial):
         """Storage API: load a historical revision of an object."""

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -275,8 +275,8 @@
     def loadBlob(self, oid, serial, version, offset):
         return self.rpc.call('loadBlob', oid, serial, version, offset)
 
-    def getSerial(self, oid):
-        return self.rpc.call('getSerial', oid)
+    def getTid(self, oid):
+        return self.rpc.call('getTid', oid)
 
     def loadSerial(self, oid, serial):
         return self.rpc.call('loadSerial', oid, serial)

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -141,8 +141,8 @@
     def __repr__(self):
         tid = self.transaction and repr(self.transaction.id)
         if self.storage:
-            stid = (self.storage._transaction and
-                    repr(self.storage._transaction.id))
+            stid = (self.storage.tpc_transaction() and
+                    repr(self.storage.tpc_transaction().id))
         else:
             stid = None
         name = self.__class__.__name__
@@ -152,14 +152,32 @@
         log(msg, level=level, label=self.log_label, exc_info=exc_info)
 
     def setup_delegation(self):
-        """Delegate several methods to the storage"""
-        self.versionEmpty = self.storage.versionEmpty
-        self.versions = self.storage.versions
-        self.getSerial = self.storage.getSerial
+        """Delegate several methods to the storage
+        """
+
+        info = self.get_info()
+        if info['supportsVersions']:
+            self.versionEmpty = self.storage.versionEmpty
+            self.versions = self.storage.versions
+            self.modifiedInVersion = self.storage.modifiedInVersion
+        else:
+            self.versionEmpty = lambda version: True
+            self.versions = lambda max=None: ()
+            self.modifiedInVersion = lambda oid: ''
+            def commitVersion(*a, **k):
+                raise NotImplementedError
+            self.commitVersion = self.abortVersion = commitVersion
+
+        if not info['supportsUndo']:
+            self.undoLog = self.undoInfo = lambda *a,**k: ()
+            def undo(*a, **k):
+                raise NotImplementedError
+            self.undo = undo
+
+        self.getTid = self.storage.getTid
         self.history = self.storage.history
         self.load = self.storage.load
         self.loadSerial = self.storage.loadSerial
-        self.modifiedInVersion = self.storage.modifiedInVersion
         record_iternext = getattr(self.storage, 'record_iternext', None)
         if record_iternext is not None:
             self.record_iternext = record_iternext
@@ -240,11 +258,27 @@
                                                                    self)
 
     def get_info(self):
-        return {'length': len(self.storage),
-                'size': self.storage.getSize(),
-                'name': self.storage.getName(),
-                'supportsUndo': self.storage.supportsUndo(),
-                'supportsVersions': self.storage.supportsVersions(),
+        storage = self.storage
+
+        try:
+            supportsVersions = storage.supportsVersions
+        except AttributeError:
+            supportsVersions = False
+        else:
+            supportsVersions = supportsVersions()
+
+        try:
+            supportsUndo = storage.supportsUndo
+        except AttributeError:
+            supportsUndo = False
+        else:
+            supportsUndo = supportsUndo()
+
+        return {'length': len(storage),
+                'size': storage.getSize(),
+                'name': storage.getName(),
+                'supportsUndo': supportsUndo,
+                'supportsVersions': supportsVersions,
                 'extensionMethods': self.getExtensionMethods(),
                 'supports_record_iternext': hasattr(self, 'record_iternext'),
                 }
@@ -292,7 +326,7 @@
 
     def verify(self, oid, version, tid):
         try:
-            t = self.storage.getTid(oid)
+            t = self.getTid(oid)
         except KeyError:
             self.client.invalidateVerify((oid, ""))
         else:
@@ -309,7 +343,7 @@
             self.verifying = 1
             self.stats.verifying_clients += 1
         try:
-            os = self.storage.getTid(oid)
+            os = self.getTid(oid)
         except KeyError:
             self.client.invalidateVerify((oid, ''))
             # It's not clear what we should do now.  The KeyError
@@ -624,7 +658,7 @@
     def _wait(self, thunk):
         # Wait for the storage lock to be acquired.
         self._thunk = thunk
-        if self.storage._transaction:
+        if self.storage.tpc_transaction():
             d = Delay()
             self.storage._waiting.append((d, self))
             self.log("Transaction blocked waiting for storage. "

Added: ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import zope.interface
+
+class IServeable(zope.interface.Interface):
+    """Interface provided by storages that can be served by ZEO
+    """
+
+    def getTid(oid):
+        """The last transaction to change an object
+
+        Return the transaction id of the last transaction that committed a
+        change to an object with the given object id.
+        
+        """
+
+    def tpc_transaction():
+        """The current transaction being committed.
+
+        If a storage is participating in a two-phase commit, then
+        return the transaction (object) being committed.  Otherwise
+        return None.
+        """
+
+    def loadEx(oid, version):
+        """Load current object data for a version
+
+        Return the current data, serial (transaction id) and version
+        for an object in a version.
+
+        If an object has been modified in the given version, then the
+        data and serial are for the most current revision of the
+        object and the returned version will match the given version.
+
+        If an object hasn't been modified in a version, or has been
+        modified in a version other than the given one, then the data,
+        and serial for the most recent non-version revision will be
+        returned along with an empty version string.
+
+        If a storage doesn't support versions, it should ignore the
+        version argument.
+        """
+        
+    def lastInvalidations(size):
+        """Get recent transaction invalidations
+
+        This method is optional and is used to get invalidations
+        performed by the most recent transactions.
+
+        An iterable of up to size entries must be returned, where each
+        entry is a transaction id and a sequence of object-id/version
+        pairs describing the objects and versions written by the
+        transaction, ordered starting at the most recent.
+        """


Property changes on: ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -34,11 +34,6 @@
             info = self._storage.undoInfo(0, 20)
         tid = info[0]['id']
 
-        # We may need to bail at this point if the storage doesn't
-        # support transactional undo
-        if not self._storage.supportsTransactionalUndo():
-            return
-
         # Now start an undo transaction
         t = Transaction()
         t.note('undo1')

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -17,7 +17,7 @@
 class FakeStorageBase:
 
     def __getattr__(self, name):
-        if name in ('versionEmpty', 'versions', 'getSerial',
+        if name in ('versionEmpty', 'versions',
                     'history', 'load', 'loadSerial', 'modifiedInVersion',
                     'lastTransaction', 'getSize', 'getName', 'supportsUndo',
                     'supportsVersions'):

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -112,16 +112,6 @@
         else:
             self._oid = oid
 
-    def abortVersion(self, src, transaction):
-        if transaction is not self._transaction:
-            raise POSException.StorageTransactionError(self, transaction)
-        return self._tid, []
-
-    def commitVersion(self, src, dest, transaction):
-        if transaction is not self._transaction:
-            raise POSException.StorageTransactionError(self, transaction)
-        return self._tid, []
-
     def close(self):
         pass
 
@@ -144,11 +134,8 @@
         return len(self)*300 # WAG!
 
     def history(self, oid, version, length=1, filter=None):
-        pass
+        return ()
 
-    def modifiedInVersion(self, oid):
-        return ''
-
     def new_oid(self):
         if self._is_read_only:
             raise POSException.ReadOnlyError()
@@ -183,12 +170,6 @@
     def isReadOnly(self):
         return self._is_read_only
 
-    def supportsUndo(self):
-        return 0
-
-    def supportsVersions(self):
-        return 0
-
     def tpc_abort(self, transaction):
         self._lock_acquire()
         try:
@@ -205,7 +186,7 @@
 
     def _abort(self):
         """Subclasses should redefine this to supply abort actions"""
-        pass
+        raise NotImplementedError
 
     def tpc_begin(self, transaction, tid=None, status=' '):
         if self._is_read_only:
@@ -243,10 +224,13 @@
         finally:
             self._lock_release()
 
+    def tpc_transaction(self):
+        return self._transaction
+
     def _begin(self, tid, u, d, e):
         """Subclasses should redefine this to supply transaction start actions.
         """
-        pass
+        raise NotImplementedError
 
     def tpc_vote(self, transaction):
         self._lock_acquire()
@@ -260,7 +244,7 @@
     def _vote(self):
         """Subclasses should redefine this to supply transaction vote actions.
         """
-        pass
+        raise NotImplementedError
 
     def tpc_finish(self, transaction, f=None):
         # It's important that the storage calls the function we pass
@@ -292,25 +276,7 @@
         """
         pass
 
-    def undo(self, transaction_id, txn):
-        if self._is_read_only:
-            raise POSException.ReadOnlyError()
-        raise POSException.UndoError('non-undoable transaction')
-
-    def undoLog(self, first, last, filter=None):
-        return ()
-
-    def versionEmpty(self, version):
-        return 1
-
-    def versions(self, max=None):
-        return ()
-
-    def pack(self, t, referencesf):
-        if self._is_read_only:
-            raise POSException.ReadOnlyError()
-
-    def getSerial(self, oid):
+    def getTid(self, oid):
         self._lock_acquire()
         try:
             v = self.modifiedInVersion(oid)

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -231,6 +231,9 @@
         if obj is not None:
             return obj
 
+        # This appears to be an MVCC violation because we are loading
+        # the must recent data when perhaps we shouldnt. The key is
+        # that we are only creating a ghost!        
         p, serial = self._storage.load(oid, self._version)
         obj = self._reader.getGhost(p)
 

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -15,6 +15,8 @@
 
 $Id$"""
 
+import warnings
+
 import cPickle, cStringIO, sys
 import threading
 from time import time, ctime
@@ -31,6 +33,7 @@
 
 import transaction
 
+
 logger = logging.getLogger('ZODB.DB')
 
 class _ConnectionPool(object):
@@ -77,23 +80,28 @@
         # a list (we push only "on the right", but may pop from both ends).
         self.available = []
 
-    # Change our belief about the expected maximum # of live connections.
-    # If the pool_size is smaller than the current value, this may discard
-    # the oldest available connections.
     def set_pool_size(self, pool_size):
+        """Change our belief about the expected maximum # of live connections.
+
+        If the pool_size is smaller than the current value, this may discard
+        the oldest available connections.
+        """
         self.pool_size = pool_size
         self._reduce_size()
 
-    # Register a new available connection.  We must not know about c already.
-    # c will be pushed onto the available stack even if we're over the
-    # pool size limit.
     def push(self, c):
+        """Register a new available connection.
+
+        We must not know about c already. c will be pushed onto the available
+        stack even if we're over the pool size limit.
+        """
         assert c not in self.all
         assert c not in self.available
         self._reduce_size(strictly_less=True)
         self.all.add(c)
         self.available.append(c)
-        n, limit = len(self.all), self.pool_size
+        n = len(self.all)
+        limit = self.pool_size
         if n > limit:
             reporter = logger.warn
             if n > 2 * limit:
@@ -101,20 +109,25 @@
             reporter("DB.open() has %s open connections with a pool_size "
                      "of %s", n, limit)
 
-    # Reregister an available connection formerly obtained via pop().  This
-    # pushes it on the stack of available connections, and may discard
-    # older available connections.
     def repush(self, c):
+        """Reregister an available connection formerly obtained via pop().
+
+        This pushes it on the stack of available connections, and may discard
+        older available connections.
+        """
         assert c in self.all
         assert c not in self.available
         self._reduce_size(strictly_less=True)
         self.available.append(c)
 
-    # Throw away the oldest available connections until we're under our
-    # target size (strictly_less=False) or no more than that (strictly_less=
-    # True, the default).
     def _reduce_size(self, strictly_less=False):
-        target = self.pool_size - bool(strictly_less)
+        """Throw away the oldest available connections until we're under our
+        target size (strictly_less=False, the default) or no more than that
+        (strictly_less=True).
+        """
+        target = self.pool_size
+        if strictly_less:
+            target -= 1
         while len(self.available) > target:
             c = self.available.pop(0)
             self.all.remove(c)
@@ -132,11 +145,13 @@
             # now, and `c` would be left in a user-visible crazy state.
             c._resetCache()
 
-    # Pop an available connection and return it, or return None if none are
-    # available.  In the latter case, the caller should create a new
-    # connection, register it via push(), and call pop() again.  The
-    # caller is responsible for serializing this sequence.
     def pop(self):
+        """Pop an available connection and return it.
+
+        Return None if none are available - in this case, the caller should
+        create a new connection, register it via push(), and call pop() again.
+        The caller is responsible for serializing this sequence.
+        """
         result = None
         if self.available:
             result = self.available.pop()
@@ -145,8 +160,8 @@
             assert result in self.all
         return result
 
-    # For every live connection c, invoke f(c).
     def map(self, f):
+        """For every live connection c, invoke f(c)."""
         self.all.map(f)
 
 class DB(object):
@@ -227,8 +242,6 @@
         self._version_pool_size = version_pool_size
         self._version_cache_size = version_cache_size
 
-        self._miv_cache = {}
-
         # Setup storage
         self._storage=storage
         self.references = ZODB.serialize.referencesf
@@ -238,7 +251,13 @@
             storage.registerDB(self, None) # Backward compat
             
         if not hasattr(storage, 'tpc_vote'):
+            warnings.warn(
+                "Storage doesn't have a tpc_vote and this violates "
+                "the stirage API. Violently monkeypatching in a do-nothing "
+                "tpc_vote.",
+                DeprecationWarning, 2)
             storage.tpc_vote = lambda *args: None
+
         try:
             storage.load(z64, '')
         except KeyError:
@@ -268,14 +287,46 @@
                              database_name)
         databases[database_name] = self
 
-        # Pass through methods:
-        for m in ['history', 'supportsUndo', 'supportsVersions', 'undoLog',
-                  'versionEmpty', 'versions']:
-            setattr(self, m, getattr(storage, m))
+        self._setupUndoMethods()
+        self._setupVersionMethods()
+        self.history = storage.history
 
-        if hasattr(storage, 'undoInfo'):
-            self.undoInfo = storage.undoInfo
+    def _setupUndoMethods(self):
+        storage = self._storage
+        try:
+            self.supportsUndo = storage.supportsUndo
+        except AttributeError:
+            self.supportsUndo = lambda : False
 
+        if self.supportsUndo():
+            self.undoLog = storage.undoLog
+            if hasattr(storage, 'undoInfo'):
+                self.undoInfo = storage.undoInfo
+        else:
+            self.undoLog = self.undoInfo = lambda *a,**k: ()
+            def undo(*a, **k):
+                raise NotImplementedError
+            self.undo = undo
+
+    def _setupVersionMethods(self):
+        storage = self._storage
+        try:
+            self.supportsVersions = storage.supportsVersions
+        except AttributeError:
+            self.supportsVersions = lambda : False
+
+        if self.supportsVersions():
+            self.versionEmpty = storage.versionEmpty
+            self.versions = storage.versions
+            self.modifiedInVersion = storage.modifiedInVersion
+        else:
+            self.versionEmpty = lambda version: True
+            self.versions = lambda max=None: ()
+            self.modifiedInVersion = lambda oid: ''
+            def commitVersion(*a, **k):
+                raise NotImplementedError
+            self.commitVersion = self.abortVersion = commitVersion
+
     # This is called by Connection.close().
     def _returnToPool(self, connection):
         """Return a connection to the pool.
@@ -471,12 +522,6 @@
         """
         if connection is not None:
             version = connection._version
-        # Update modified in version cache
-        for oid in oids:
-            h = hash(oid) % 131
-            o = self._miv_cache.get(h, None)
-            if o is not None and o[0]==oid:
-                del self._miv_cache[h]
 
         # Notify connections.
         def inval(c):
@@ -487,20 +532,9 @@
 
     def invalidateCache(self):
         """Invalidate each of the connection caches
-        """        
-        self._miv_cache.clear()
+        """
         self._connectionMap(lambda c: c.invalidateCache())
 
-    def modifiedInVersion(self, oid):
-        h = hash(oid) % 131
-        cache = self._miv_cache
-        o = cache.get(h, None)
-        if o and o[0] == oid:
-            return o[1]
-        v = self._storage.modifiedInVersion(oid)
-        cache[h] = oid, v
-        return v
-
     def objectCount(self):
         return len(self._storage)
 
@@ -687,17 +721,18 @@
             txn = transaction.get()
         txn.register(TransactionalUndo(self, id))
 
-    def versionEmpty(self, version):
-        return self._storage.versionEmpty(version)
 
-
-
 resource_counter_lock = threading.Lock()
 resource_counter = 0
 
 class ResourceManager(object):
     """Transaction participation for a version or undo resource."""
 
+    # XXX This implementation is broken.  Subclasses invalidate oids
+    # in their commit calls. Invalidations should not be sent until
+    # tpc_finish is called.  In fact, invalidations should be sent to
+    # the db *while* tpc_finish is being called on the storage.
+
     def __init__(self, db):
         self._db = db
         # Delegate the actual 2PC methods to the storage
@@ -729,10 +764,10 @@
     # argument to the methods below is self.
 
     def abort(self, obj, txn):
-        pass
+        raise NotImplementedError
 
     def commit(self, obj, txn):
-        pass
+        raise NotImplementedError
 
 class CommitVersion(ResourceManager):
 
@@ -742,6 +777,7 @@
         self._dest = dest
 
     def commit(self, ob, t):
+        # XXX see XXX in ResourceManager
         dest = self._dest
         tid, oids = self._db._storage.commitVersion(self._version,
                                                     self._dest,
@@ -760,6 +796,7 @@
         self._version = version
 
     def commit(self, ob, t):
+        # XXX see XXX in ResourceManager
         tid, oids = self._db._storage.abortVersion(self._version, t)
         self._db.invalidate(tid,
                             dict.fromkeys(oids, 1),
@@ -772,5 +809,6 @@
         self._tid = tid
 
     def commit(self, ob, t):
+        # XXX see XXX in ResourceManager
         tid, oids = self._db._storage.undo(self._tid, t)
         self._db.invalidate(tid, dict.fromkeys(oids, 1))

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -213,13 +213,14 @@
             self._lock_release()
 
     def loadEx(self, oid, version):
+        raise TypeError("untested")
         self._lock_acquire()
         try:
             try:
                 oid, pre, vdata, p, tid = self._index[oid]
             except KeyError:
                 if self._base:
-                    return self._base.load(oid, '')
+                    return self._base.loadEx(oid, '')
                 raise KeyError(oid)
 
             ver = ""

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -940,9 +940,6 @@
             self._file.truncate(self._pos)
             self._nextpos=0
 
-    def supportsTransactionalUndo(self):
-        return 1
-
     def _undoDataInfo(self, oid, pos, tpos):
         """Return the tid, data pointer, data, and version for the oid
         record at pos"""
@@ -1440,7 +1437,10 @@
         except ValueError: # "empty tree" error
             next_oid = None
 
-        data, tid = self.load(oid, "") # ignore versions
+        # ignore versions
+        # XXX if the object was created in a version, this will fail.
+        data, tid = self.load(oid, "")
+
         return oid, tid, data, next_oid
 
 

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -18,6 +18,7 @@
 
 from zope.interface import Interface, Attribute
 
+
 class IConnection(Interface):
     """Connection to ZODB for loading and storing objects.
 
@@ -289,7 +290,6 @@
         This invalidates *all* objects in the cache. If the connection
         is open, subsequent reads will fail until a new transaction
         begins or until the connection os reopned.
-        
         """
 
 class IStorageDB(Interface):
@@ -345,37 +345,6 @@
     TODO: This interface is incomplete.
     """
 
-## __init__ methods don't belong in interfaces:
-##
-##     def __init__(storage,
-##                  pool_size=7,
-##                  cache_size=400,
-##                  version_pool_size=3,
-##                  version_cache_size=100,
-##                  database_name='unnamed',
-##                  databases=None,
-##                  ):
-##         """Create an object database.
-
-##         storage: the storage used by the database, e.g. FileStorage
-##         pool_size: expected maximum number of open connections
-##         cache_size: target size of Connection object cache, in number of
-##             objects
-##         version_pool_size: expected maximum number of connections (per
-##             version)
-##         version_cache_size: target size of Connection object cache for
-##              version connections, in number of objects
-##         database_name: when using a multi-database, the name of this DB
-##             within the database group.  It's a (detected) error if databases
-##             is specified too and database_name is already a key in it.
-##             This becomes the value of the DB's database_name attribute.
-##         databases: when using a multi-database, a mapping to use as the
-##             binding of this DB's .databases attribute.  It's intended
-##             that the second and following DB's added to a multi-database
-##             pass the .databases attribute set on the first DB added to the
-##             collection.
-##         """
-
     databases = Attribute("""\
         A mapping from database name to DB (database) object.
 
@@ -386,119 +355,455 @@
         entry.
         """)
 
-    def invalidateCache():
-        """Invalidate all objects in the database object caches
+    def open(version='',
+             mvcc=True,
+             transaction_manager=None,
+             synch=True
+             ):
+        """Return an IConnection object for use by application code.
 
-        invalidateCache will be called on each of the database's connections.
+        version: the "version" that all changes will be made
+            in, defaults to no version.
+        mvcc: boolean indicating whether MVCC is enabled
+        transaction_manager: transaction manager to use.  None means
+            use the default transaction manager.
+        synch: boolean indicating whether Connection should
+            register for afterCompletion() calls.
+
+        Note that the connection pool is managed as a stack, to
+        increase the likelihood that the connection's stack will
+        include useful objects.
         """
 
+    # TODO: Should this method be moved into some subinterface?
+    def pack(t=None, days=0):
+        """Pack the storage, deleting unused object revisions.
+
+        A pack is always performed relative to a particular time, by
+        default the current time.  All object revisions that are not
+        reachable as of the pack time are deleted from the storage.
+
+        The cost of this operation varies by storage, but it is
+        usually an expensive operation.
+
+        There are two optional arguments that can be used to set the
+        pack time: t, pack time in seconds since the epcoh, and days,
+        the number of days to subtract from t or from the current
+        time if t is not specified.
+        """
+
+    # TODO: Should this method be moved into some subinterface?
+    def undo(id, txn=None):
+        """Undo a transaction identified by id.
+
+        A transaction can be undone if all of the objects involved in
+        the transaction were not modified subsequently, if any
+        modifications can be resolved by conflict resolution, or if
+        subsequent changes resulted in the same object state.
+
+        The value of id should be generated by calling undoLog()
+        or undoInfo().  The value of id is not the same as a
+        transaction id used by other methods; it is unique to undo().
+
+        id: a storage-specific transaction identifier
+        txn: transaction context to use for undo().
+            By default, uses the current transaction.
+        """
+
+    def close():
+        """Close the database and its underlying storage.
+
+        It is important to close the database, because the storage may
+        flush in-memory data structures to disk when it is closed.
+        Leaving the storage open with the process exits can cause the
+        next open to be slow.
+
+        What effect does closing the database have on existing
+        connections?  Technically, they remain open, but their storage
+        is closed, so they stop behaving usefully.  Perhaps close()
+        should also close all the Connections.
+        """
+
 class IStorage(Interface):
     """A storage is responsible for storing and retrieving data of objects.
     """
 
-## What follows is the union of methods found across various storage
-## implementations.  Exactly what "the storage API" is and means has
-## become fuzzy over time.  Methods should be uncommented here, or
-## even deleted, as the storage API regains a crisp definition.
+    def close():
+        """Close the storage.
+        """
 
-##    def load(oid, version):
-##        """TODO"""
-##
-##    def close():
-##        """TODO"""
-##
-##    def cleanup():
-##        """TODO"""
-##
-##    def lastSerial():
-##        """TODO"""
-##
-##    def lastTransaction():
-##        """TODO"""
-##
-##    def lastTid(oid):
-##        """Return last serialno committed for object oid."""
-##
-##    def loadSerial(oid, serial):
-##        """TODO"""
-##
-##    def loadBefore(oid, tid):
-##        """TODO"""
-##
-##    def iterator(start=None, stop=None):
-##        """TODO"""
-##
-##    def sortKey():
-##        """TODO"""
-##
-##    def getName():
-##        """TODO"""
-##
-##    def getSize():
-##        """TODO"""
-##
-##    def history(oid, version, length=1, filter=None):
-##        """TODO"""
-##
-##    def new_oid():
-##        """TODO"""
-##
-##    def set_max_oid(possible_new_max_oid):
-##        """TODO"""
-##
-##    def registerDB(db):
-##        """TODO"""
-##
-##    def isReadOnly():
-##        """TODO"""
-##
-##    def supportsUndo():
-##        """TODO"""
-##
-##    def supportsVersions():
-##        """TODO"""
-##
-##    def tpc_abort(transaction):
-##        """TODO"""
-##
-##    def tpc_begin(transaction):
-##        """TODO"""
-##
-##    def tpc_vote(transaction):
-##        """TODO"""
-##
-##    def tpc_finish(transaction, f=None):
-##        """TODO"""
-##
-##    def getSerial(oid):
-##        """TODO"""
-##
-##    def loadSerial(oid, serial):
-##        """TODO"""
-##
-##    def loadBefore(oid, tid):
-##        """TODO"""
-##
-##    def getExtensionMethods():
-##        """TODO"""
-##
-##    def copyTransactionsFrom():
-##        """TODO"""
-##
-##    def store(oid, oldserial, data, version, transaction):
-##        """
-##
-##        may return the new serial or not
-##        """
+    def getName():
+        """The name of the storage
 
+        The format and interpretation of this name is storage
+        dependent. It could be a file name, a database name, etc.
+
+        This is used soley for informational purposes.
+        """
+
+    def getSize():
+        """An approximate size of the database, in bytes.
+        
+        This is used soley for informational purposes.
+        """
+
+    def history(oid, version, size=1):
+        """Return a sequence of history information dictionaries.
+
+        Up to size objects (including no objects) may be returned.
+        
+        The information provides a log of the changes made to the
+        object. Data are reported in reverse chronological order.
+
+        Each dictionary has the following keys:
+
+        time
+            UTC seconds since the epoch (as in time.time) that the
+            object revision was committed.
+        tid
+            The transaction identifier of the transaction that
+            committed the version.
+        version
+            The version that the revision is in.  If the storage
+            doesn't support versions, then this must be an empty
+            string.
+        user_name
+            The user identifier, if any (or an empty string) of the
+            user on whos behalf the revision was committed.
+        description
+            The transaction description for the transaction that
+            committed the revision.
+        size
+            The size of the revision data record.
+
+        If the transaction had extension items, then these items are
+        also included if they don't conflict with the keys above.
+        """
+
+    def isReadOnly():
+        """Test whether a storage allows committing new transactions
+
+        For a given storage instance, this method always returns the
+        same value.  Read-only-ness is a static property of a storage.
+        """
+
+    def lastTransaction():
+        """Return the id of the last committed transaction
+        """
+
+    def __len__():
+        """The approximate number of objects in the storage
+        
+        This is used soley for informational purposes.
+        """
+
+    def load(oid, version):
+        """Load data for an object id and version
+
+        A data record and serial are returned.  The serial is a
+        transaction identifier of the transaction that wrote the data
+        record.
+
+        A POSKeyError is raised if there is no record for the object
+        id and version.
+
+        Storages that don't support versions must ignore the version
+        argument.
+        """
+
+    def loadBefore(oid, tid):
+        """Load the object data written before a transaction id
+
+        If there isn't data before the object before the given
+        transaction, then None is returned, otherwise three values are
+        returned:
+
+        - The data record
+
+        - The transaction id of the data record
+
+        - The transaction id of the following revision, if any, or None.
+        """
+
+    def loadSerial(oid, serial):
+        """Load the object record for the give transaction id
+
+        A data record is returned.
+        """
+
+    def new_oid():
+        """Allocate a new object id.
+
+        The object id returned is reserved at least as long as the
+        storage is opened.
+
+        The return value is a string.
+        """
+
+    def pack(pack_time, referencesf):
+        """Pack the storage
+
+        It is up to the storage to interpret this call, however, the
+        general idea is that the storage free space by:
+
+        - discarding object revisions that were old and not current as of the
+          given pack time.
+
+        - garbage collecting objects that aren't reachable from the
+          root object via revisions remaining after discarding
+          revisions that were not current as of the pack time.
+
+        The pack time is given as a UTC time in seconds since the
+        empoch.
+
+        The second argument is a function that should be used to
+        extract object references from database records.  This is
+        needed to determine which objects are referenced from object
+        revisions.
+        """
+
+    def registerDB(db):
+        """Register an IStorageDB.
+
+        Note that, for historical reasons, an implementation may
+        require a second argument, however, if required, the None will
+        be passed as the second argument.
+        """
+
+    def sortKey():
+        """Sort key used to order distributed transactions
+
+        When a transaction involved multiple storages, 2-phase commit
+        operations are applied in sort-key order.  This must be unique
+        among storages used in a transaction. Obviously, the storage
+        can't assure this, but it should construct the sort key so it
+        has a reasonable chance of being unique.
+        """
+
+    def store(oid, serial, data, version, transaction):
+        """Store data for the object id, oid.
+
+        Arguments:
+
+        oid
+            The object identifier.  This is either a string
+            consisting of 8 nulls or a string previously returned by
+            new_oid. 
+
+        serial
+            The serial of the data that was read when the object was
+            loaded from the database.  If the object was created in
+            the current transaction this will be a string consisting
+            of 8 nulls.
+
+        data
+            The data record. This is opaque to the storage.
+
+        version
+            The version to store the data is.  If the storage doesn't
+            support versions, this should be an empty string and the
+            storage is allowed to ignore it.
+
+        transaction
+            A transaction object.  This should match the current
+            transaction for the storage, set by tpc_begin.
+
+        The new serial for the object is returned, but not necessarily
+        immediately.  It may be returned directly, or un a subsequent
+        store or tpc_vote call.
+
+        The return value may be:
+
+        - None
+
+        - A new serial (string) for the object, or
+
+        - An iterable of object-id and serial pairs giving new serials
+          for objects.
+        """
+
+    def tpc_abort(transaction):
+        """Abort the transaction.
+
+        Any changes made by the transaction are discarded.
+
+        This call is ignored is the storage is not participating in
+        two-phase commit or if the given transaction is not the same
+        as the transaction the storage is commiting.
+        """
+
+    def tpc_begin(transaction):
+        """Begin the two-phase commit process.
+
+        If storage is already participating in a two-phase commit
+        using the same transaction, the call is ignored.
+
+        If the storage is already participating in a two-phase commit
+        using a different transaction, the call blocks until the
+        current transaction ends (commits or aborts).
+        """
+
+    def tpc_finish(transaction, func = lambda: None):
+        """Finish the transaction, making any transaction changes permanent.
+
+        Changes must be made permanent at this point.
+
+        This call is ignored if the storage isn't participating in
+        two-phase commit or if it is commiting a different
+        transaction.  Failure of this method is extremely serious.
+        """
+
+    def tpc_vote(transaction):
+        """Provide a storage with an opportunity to veto a transaction
+
+        This call is ignored if the storage isn't participating in
+        two-phase commit or if it is commiting a different
+        transaction.  Failure of this method is extremely serious.
+
+        If a transaction can be committed by a storage, then the
+        method should return.  If a transaction cannot be committed,
+        then an exception should be raised.  If this method returns
+        without an error, then there must not be an error if
+        tpc_finish or tpc_abort is called subsequently.
+
+        The return value can be either None or a sequence of object-id
+        and serial pairs giving new serials for objects who's ids were
+        passed to previous store calls in the same transaction.
+        After the tpc_vote call, bew serials must have been returned,
+        either from tpc_vote or store for objects passed to store.
+        """
+
+class IStorageRestoreable(IStorage):
+
+    def tpc_begin(transaction, tid=None):
+        """Begin the two-phase commit process.
+
+        If storage is already participating in a two-phase commit
+        using the same transaction, the call is ignored.
+
+        If the storage is already participating in a two-phase commit
+        using a different transaction, the call blocks until the
+        current transaction ends (commits or aborts).
+
+        If a transaction id is given, then the transaction will use
+        the given id rather than generating a new id.  This is used
+        when copying already committed transactions from another
+        storage.
+        """
+
+        # Note that the current implementation also accepts a status.
+        # This is an artifact of:
+        # - Earlier use of an undo status to undo revisions in place,
+        #   and,
+        # - Incorrect pack garbage-collection algorithms (possibly
+        #   including the existing FileStorage implementation), that
+        #   failed to take into account records after the pack time.
+        
+
+    def restore(oid, serial, data, version, prev_txn, transaction):
+        """Write data already committed in a separate database
+
+        The restore method is used when copying data from one database
+        to a replica of the database.  It differs from store in that
+        the data have already been committed, so there is no check for
+        conflicts and no new transaction is is used for the data.
+
+        Arguments:
+
+        oid
+             The object id for the record
+        
+        serial
+             The transaction identifier that originally committed this object.
+
+        data
+             The record data.  This will be None if the transaction
+             undid the creation of the object.
+
+        version
+             The version identifier for the record
+
+        prev_txn
+             The identifier of a previous transaction that held the
+             object data.  The target storage can sometimes use this
+             as a hint to save space.
+
+        transaction
+             The current transaction.
+
+        Nothing is returned.
+        """
+
+class IStorageRecordInformation(Interface):
+    """Provide information about a single storage record
+    """
+
+    oid = Attribute("The object id")
+    version = Attribute("The version")
+    data = Attribute("The data record")
+
+class IStorageTransactionInformation(Interface):
+    """Provide information about a storage transaction
+    """
+
+    tid = Attribute("Transaction id")
+    status = Attribute("Transaction Status") # XXX what are valid values?
+    user = Attribute("Transaction user")
+    description = Attribute("Transaction Description")
+    extension = Attribute("Transaction extension data")
+
+    def __iter__():
+        """Return an iterable of IStorageTransactionInformation
+        """
+
+class IStorageIteration(Interface):
+    """API for iterating over the contents of a storage
+
+    Note that this is a future API.  Some storages now provide an
+    approximation of this.
+
+    """
+
+    def iterator(start=None, stop=None):
+        """Return an IStorageTransactionInformation iterator.
+
+        An IStorageTransactionInformation iterator is returned for
+        iterating over the transactions in the storage.
+
+        If the start argument is not None, then iteration will start
+        with the first transaction whos identifier is greater than or
+        equal to start.
+
+        If the stop argument is not None, then iteration will end with
+        the last transaction whos identifier is less than or equal to
+        start.
+
+        """
+
 class IStorageUndoable(IStorage):
     """A storage supporting transactional undo.
     """
 
-    def undo(transaction_id, txn):
-        """TODO"""
+    def supportsUndo():
+        """Return True, indicating that the storage supports undo.
+        """
 
-    def undoLog(first, last, filter=(lambda desc: True)):
+    def undo(transaction_id, transaction):
+        """Undo the transaction corresponding to the given transaction id.
+
+        The transaction id is a value returned from undoInfo or
+        undoLog, which may not be a stored transaction identifier as
+        used elsewhere in the storage APIs.
+
+        This method must only be called in the first phase of
+        two-phase commit (after tpc_begin but before tpc_vote). It
+        returns a serial (transaction id) and a sequence of object ids
+        for objects affected by the transaction.
+
+        """
+        # Used by DB (Actually, by TransactionalUndo)
+
+    def undoLog(first, last, filter=None):
         """Return a sequence of descriptions for undoable transactions.
 
         Application code should call undoLog() on a DB instance instead of on
@@ -551,8 +856,9 @@
                      could be gotten by passing the positive first-last for
                      `last` instead.
         """
+        # DB pass through
 
-    def undoInfo(first, last, specification=None):
+    def undoInfo(first=0, last=-20, specification=None):
         """Return a sequence of descriptions for undoable transactions.
 
         This is like `undoLog()`, except for the `specification` argument.
@@ -567,30 +873,84 @@
         ZEO client to its ZEO server (while a ZEO client ignores any `filter`
         argument passed to `undoLog()`).
         """
+        # DB pass-through
 
+
+class IStoragePackable(Interface):
+
     def pack(t, referencesf):
-        """TODO"""
+        """Pack the storage
 
+        Pack and/or garbage-collect the storage. If the storage does
+        not support undo, then t is ignored. All records for objects
+        that are not reachable from the system root object as of time
+        t, or as of the current time, if undo is not supported, are
+        removed from the storage.
+
+        A storage implementation may treat this method as ano-op. A
+        storage implementation may also delay packing and return
+        immediately. Storage documentation should define the behavior
+        of this method.
+        """
+        # Called by DB
+
+class IStorageCurrentRecordIteration(IStorage):
+
+    def record_iternext(next=None):
+        """Iterate over the records in a storage
+
+        Use like this:
+
+            >>> next = None
+            >>> while 1:
+            ...     oid, tid, data, next = storage.record_iternext(next)
+            ...     # do things with oid, tid, and data
+            ...     if next is None:
+            ...         break
+        
+        """
+
 class IStorageVersioning(IStorage):
     """A storage supporting versions.
+
+    It is likely that version support will disappear from future
+    versions of ZODB.  There are known bugs in version handling.
     """
 
-## What follows is the union of methods found across various version storage
-## implementations.  Exactly what "the storage API" is and means has
-## become fuzzy over time.  Methods should be uncommented here, or
-## even deleted, as the storage API regains a crisp definition.
+    def supportsVersions():
+        """Return True, idicating that the storage suports versions.
+        """
 
-##    def abortVersion(src, transaction):
-##        """TODO"""
-##
-##    def commitVersion(src, dest, transaction):
-##        """TODO"""
-##
-##    def modifiedInVersion(oid):
-##        """TODO"""
-##
-##    def versionEmpty(version):
-##        """TODO"""
-##
-##    def versions(max=None):
-##        """TODO"""
+    def abortVersion(version, transaction):
+        """Clear any changes made by the given version.
+        """
+        # used by DB
+
+    def commitVersion(source, destination, transaction):
+        """Save version changes
+
+        Store changes made in the source version into the destination
+        version. A VersionCommitError is raised if the source and
+        destination are equal or if the source is an empty string. The
+        destination may be an empty string, in which case the data are
+        saved to non-version storage.
+        """
+        # used by DB
+
+    def versionEmpty(version):
+        """true if the version for the given version string is empty.
+        """
+        # DB pass through
+
+    def modifiedInVersion(oid):
+        """the version that the object was modified in,
+
+        or an empty string if the object was not modified in a version
+        """
+        # DB pass through, sor of.  In the past (including present :),
+        # the DB tried to cache this.  We'll probably stop bothering.
+
+    def versions(max = None):
+        """A sequence of version strings for active versions
+        """
+        # DB pass through

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -161,19 +161,19 @@
         revid4 = self._dostore(oid2, revid=revid2, data=p52)
         noteq(revid3, revid4)
 
-    def checkGetSerial(self):
-        if not hasattr(self._storage, 'getSerial'):
+    def checkGetTid(self):
+        if not hasattr(self._storage, 'getTid'):
             return
         eq = self.assertEqual
         p41, p42 = map(MinPO, (41, 42))
         oid = self._storage.new_oid()
-        self.assertRaises(KeyError, self._storage.getSerial, oid)
+        self.assertRaises(KeyError, self._storage.getTid, oid)
         # Now store a revision
         revid1 = self._dostore(oid, data=p41)
-        eq(revid1, self._storage.getSerial(oid))
+        eq(revid1, self._storage.getTid(oid))
         # And another one
         revid2 = self._dostore(oid, revid=revid1, data=p42)
-        eq(revid2, self._storage.getSerial(oid))
+        eq(revid2, self._storage.getTid(oid))
 
     def checkTwoArgBegin(self):
         # Unsure: how standard is three-argument tpc_begin()?

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -87,11 +87,6 @@
                 pass
 
     def checkUndoZombieNonVersion(self):
-        if not hasattr(self._storage, 'supportsTransactionalUndo'):
-            return
-        if not self._storage.supportsTransactionalUndo():
-            return
-
         oid = self._storage.new_oid()
         revid = self._dostore(oid, data=MinPO(94))
         # Get the undo information

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -59,6 +59,5 @@
         self.assertRaises(ReadOnlyError, self._storage.store,
                           '\000' * 8, None, '', '', t)
 
-        if self._storage.supportsTransactionalUndo():
-            self.assertRaises(ReadOnlyError, self._storage.undo,
-                              '\000' * 8, t)
+        self.assertRaises(ReadOnlyError, self._storage.undo,
+                          '\000' * 8, t)

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -143,7 +143,7 @@
         eq(zodb_unpickle(data), MinPO(23))
         self._iterate()
 
-    def checkCreationUndoneGetSerial(self):
+    def checkCreationUndoneGetTid(self):
         # create an object
         oid = self._storage.new_oid()
         self._dostore(oid, data=MinPO(23))
@@ -156,9 +156,9 @@
         self._storage.undo(tid, t)
         self._storage.tpc_vote(t)
         self._storage.tpc_finish(t)
-        # Check that calling getSerial on an uncreated object raises a KeyError
+        # Check that calling getTid on an uncreated object raises a KeyError
         # The current version of FileStorage fails this test
-        self.assertRaises(KeyError, self._storage.getSerial, oid)
+        self.assertRaises(KeyError, self._storage.getTid, oid)
 
     def checkUndoCreationBranch1(self):
         eq = self.assertEqual

Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py	2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py	2007-04-24 23:35:31 UTC (rev 74732)
@@ -80,8 +80,8 @@
         eq(zodb_unpickle(data), MinPO(12))
         data, vrevid = self._storage.load(oid, version)
         eq(zodb_unpickle(data), MinPO(15))
-        if hasattr(self._storage, 'getSerial'):
-            s = self._storage.getSerial(oid)
+        if hasattr(self._storage, 'getTid'):
+            s = self._storage.getTid(oid)
             eq(s, max(revid, vrevid))
         data, tid, ver = self._storage.loadEx(oid, version)
         eq(zodb_unpickle(data), MinPO(15))
@@ -186,7 +186,7 @@
         eq = self.assertEqual
         oid, version = self._setup_version()
 
-        # Not sure I can write a test for getSerial() in the
+        # Not sure I can write a test for getTid() in the
         # presence of aborted versions, because FileStorage and
         # Berkeley storage give a different answer. I think Berkeley
         # is right and FS is wrong.
@@ -219,11 +219,9 @@
         self._storage.tpc_begin(t)
 
         # And try to abort the empty version
-        if (hasattr(self._storage, 'supportsTransactionalUndo') and
-                self._storage.supportsTransactionalUndo()):
-            self.assertRaises(POSException.VersionError,
-                              self._storage.abortVersion,
-                              '', t)
+        self.assertRaises(POSException.VersionError,
+                          self._storage.abortVersion,
+                          '', t)
 
         # But now we really try to abort the version
         tid, oids = self._storage.abortVersion(version, t)
@@ -235,9 +233,6 @@
         eq(zodb_unpickle(data), MinPO(51))
 
     def checkCommitVersionErrors(self):
-        if not (hasattr(self._storage, 'supportsTransactionalUndo') and
-                self._storage.supportsTransactionalUndo()):
-            return
         eq = self.assertEqual
         oid1, version1 = self._setup_version('one')
         data, revid1 = self._storage.load(oid1, version1)



More information about the Zodb-checkins mailing list