[Checkins] SVN: ZODB/trunk/src/ZODB/ Added an option to disallow cross-database references.

Jim Fulton jim at zope.com
Thu Apr 30 07:43:45 EDT 2009


Log message for revision 99602:
  Added an option to disallow cross-database references.
  
  Include extra data in InvalidObjectReference exceptions to make
  debugging easier.
  

Changed:
  U   ZODB/trunk/src/ZODB/component.xml
  U   ZODB/trunk/src/ZODB/cross-database-references.txt
  U   ZODB/trunk/src/ZODB/serialize.py

-=-
Modified: ZODB/trunk/src/ZODB/component.xml
===================================================================
--- ZODB/trunk/src/ZODB/component.xml	2009-04-30 11:43:43 UTC (rev 99601)
+++ ZODB/trunk/src/ZODB/component.xml	2009-04-30 11:43:45 UTC (rev 99602)
@@ -113,7 +113,7 @@
             Maximum size of the ZEO blob cache, in bytes.  If not set, then
             the cache size isn't checked and the blob directory will
             grow without bound.
-            
+
             This option is ignored if shared_blob_dir is true.
       </description>
     </key>
@@ -125,7 +125,6 @@
             size.   This option is ignored if shared_blob_dir is true.
       </description>
     </key>
-    
     <key name="storage" default="1">
       <description>
         The name of the storage that the client wants to use.  If the
@@ -272,12 +271,13 @@
       </description>
     </key>
     <key name="historical-timeout" datatype="time-interval"
-         default="5m"/>
+         default="5m">
       <description>
         The minimum interval that an unused historical connection should be
         kept.
       </description>
-    <key name="database-name" default="unnamed"/>
+    </key>
+    <key name="database-name">
       <description>
         When multidatabases are in use, this is the name given to this
         database in the collection.  The name must be unique across all
@@ -288,6 +288,14 @@
         their own config files, using the "databases" parameter of a DB
         constructor.
       </description>
+    </key>
+    <key name="allow-implicit-cross-references" datatype="boolean">
+      <description>
+        If set to false, implicit cross references (the only kind
+        currently possible) are disallowed.
+      </description>
+    </key>
+
   </sectiontype>
 
   <sectiontype name="blobstorage" datatype=".BlobStorage"
@@ -301,7 +309,5 @@
   </sectiontype>
 
 
-    
 
-
 </component>

Modified: ZODB/trunk/src/ZODB/cross-database-references.txt
===================================================================
--- ZODB/trunk/src/ZODB/cross-database-references.txt	2009-04-30 11:43:43 UTC (rev 99601)
+++ ZODB/trunk/src/ZODB/cross-database-references.txt	2009-04-30 11:43:45 UTC (rev 99602)
@@ -58,11 +58,13 @@
     >>> tm.commit()
 
     >>> p2.p3 = p3
-    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
+    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
     Traceback (most recent call last):
     ...
     InvalidObjectReference:
-    Attempt to store an object from a foreign database connection
+      ('Attempt to store an object from a foreign database connection',
+       <Connection at ...>,
+       <ZODB.tests.testcrossdatabasereferences.MyClass...>)
 
     >>> tm.abort()
 
@@ -84,11 +86,14 @@
 an error is generated if we commit changes when new objects are
 reachable from multiple databases:
 
-    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
+    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
     Traceback (most recent call last):
     ...
-    InvalidObjectReference: A new object is reachable from multiple
-    databases. Won't try to guess which one was correct!
+    InvalidObjectReference:
+    ("A new object is reachable from multiple databases. Won't try to
+    guess which one was correct!",
+    <Connection at ...>,
+    <ZODB.tests.testcrossdatabasereferences.MyClass...>)
 
     >>> tm.abort()
 
@@ -109,11 +114,14 @@
     >>> p1.p5 = p5
     >>> s = tm.savepoint()
     >>> p2.p5 = p5
-    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
+    >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
     Traceback (most recent call last):
     ...
-    InvalidObjectReference: A new object is reachable from multiple
-    databases. Won't try to guess which one was correct!
+    InvalidObjectReference:
+    ("A new object is reachable from multiple databases. Won't try to guess
+    which one was correct!",
+    <Connection at ...>,
+    <ZODB.tests.testcrossdatabasereferences.MyClass...>)
 
     >>> tm.abort()
 
@@ -133,6 +141,37 @@
 This the most explicit and thus the best way, when practical, to avoid
 the ambiguity.
 
+Dissallowing implicit cross-database references
+-----------------------------------------------
+
+The database contructor accepts a xrefs keyword argument that defaults
+to True.  If False is passed, the implicit cross database references
+are disallowed. (Note that currently, implicit cross references are
+the only kind of cross references allowed.)
+
+    >>> databases = {}
+    >>> db1 = ZODB.tests.util.DB(databases=databases, database_name='1')
+    >>> db2 = ZODB.tests.util.DB(databases=databases, database_name='2',
+    ...                          xrefs=False)
+
+In this example, we allow cross-references from db1 to db2, but not
+the other way around.
+
+    >>> c1 = db1.open()
+    >>> c2 = c1.get_connection('2')
+    >>> c1.root.x = c2.root()
+    >>> transaction.commit()
+    >>> c2.root.x = c1.root()
+    >>> transaction.commit() # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    Traceback (most recent call last):
+    ...
+    InvalidObjectReference:
+    ("Database '2' doesn't allow implicit cross-database references",
+    <Connection at ...>,
+    {'x': {}})
+
+    >>> transaction.abort()
+
 NOTE
 ----
 

Modified: ZODB/trunk/src/ZODB/serialize.py
===================================================================
--- ZODB/trunk/src/ZODB/serialize.py	2009-04-30 11:43:43 UTC (rev 99601)
+++ ZODB/trunk/src/ZODB/serialize.py	2009-04-30 11:43:45 UTC (rev 99602)
@@ -186,6 +186,7 @@
 
         >>> from ZODB.tests.util import P
         >>> class DummyJar:
+        ...     xrefs = True
         ...     def new_oid(self):
         ...         return 42
         ...     def db(self):
@@ -229,11 +230,13 @@
         If the jar doesn't match that of the writer, an error is raised:
 
         >>> bob._p_jar = DummyJar()
-        >>> writer.persistent_id(bob)   # doctest: +NORMALIZE_WHITESPACE
+        >>> writer.persistent_id(bob)
+        ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
         Traceback (most recent call last):
           ...
-        InvalidObjectReference: Attempt to store an object from a
-            foreign database connection
+        InvalidObjectReference:
+        ('Attempt to store an object from a foreign database connection',
+        <ZODB.serialize.DummyJar instance at ...>, P(bob))
 
         Constructor arguments used by __new__(), as returned by
         __getnewargs__(), can affect memory allocation, but may also
@@ -322,8 +325,13 @@
             oid = obj._p_oid = self._jar.new_oid()
             obj._p_jar = self._jar
             self._stack.append(obj)
-            
+
         elif obj._p_jar is not self._jar:
+            if not self._jar.db().xrefs:
+                raise InvalidObjectReference(
+                    "Database %r doesn't allow implicit cross-database "
+                    "references" % self._jar.db().database_name,
+                    self._jar, obj)
 
             try:
                 otherdb = obj._p_jar.db()
@@ -334,14 +342,14 @@
             if self._jar.db().databases.get(database_name) is not otherdb:
                 raise InvalidObjectReference(
                     "Attempt to store an object from a foreign "
-                    "database connection"
+                    "database connection", self._jar, obj,
                     )
 
             if self._jar.get_connection(database_name) is not obj._p_jar:
                 raise InvalidObjectReference(
                     "Attempt to store a reference to an object from "
                     "a separate connection to the same database or "
-                    "multidatabase"
+                    "multidatabase", self._jar, obj,
                     )
 
             # OK, we have an object from another database.
@@ -350,9 +358,9 @@
             if obj._p_jar._implicitlyAdding(oid):
                 raise InvalidObjectReference(
                     "A new object is reachable from multiple databases. "
-                    "Won't try to guess which one was correct!"
+                    "Won't try to guess which one was correct!",
+                    self._jar, obj,
                     )
-                
 
         klass = type(obj)
         if hasattr(klass, '__getnewargs__'):



More information about the Checkins mailing list