Commit a8a91c72 authored by Jim Fulton's avatar Jim Fulton

Fixed the basic tests to open the second db correctly.

Secondary connections always need to be made from an existing
connection.

Added check to make sure we don't get references across incompatible
connections.
parent 68ef2b04
...@@ -19,11 +19,17 @@ And create a persistent object in the first database: ...@@ -19,11 +19,17 @@ And create a persistent object in the first database:
>>> conn1.root()['p'] = p1 >>> conn1.root()['p'] = p1
>>> tm.commit() >>> tm.commit()
First, we get a connection to the second database. We get the second
connection using the first connection's `get_connextion` method. This
is important. When using multiple databases, we need to make sure we
use a consistent set of connections so that the objects in the
connection caches are connected in a consistent manner.
>>> conn2 = conn1.get_connection('2')
Now, we'll create a second persistent object in the second database. Now, we'll create a second persistent object in the second database.
We'll have a reference to the first object: We'll have a reference to the first object:
>>> tm = transaction.TransactionManager()
>>> conn2 = db2.open(transaction_manager=tm)
>>> p2 = MyClass() >>> p2 = MyClass()
>>> conn2.root()['p'] = p2 >>> conn2.root()['p'] = p2
>>> p2.p1 = p1 >>> p2.p1 = p1
......
...@@ -337,6 +337,14 @@ class ObjectWriter: ...@@ -337,6 +337,14 @@ class ObjectWriter:
"database connection" "database connection"
) )
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 onnection to the same database or "
"multidatabase"
)
klass = type(obj) klass = type(obj)
if hasattr(klass, '__getnewargs__'): if hasattr(klass, '__getnewargs__'):
# We don't want to save newargs in object refs. # We don't want to save newargs in object refs.
......
...@@ -26,6 +26,59 @@ class MyClass_w_getnewargs(persistent.Persistent): ...@@ -26,6 +26,59 @@ class MyClass_w_getnewargs(persistent.Persistent):
def __getnewargs__(self): def __getnewargs__(self):
return () return ()
def test_must_use_consistent_connections():
"""
It's important to use consistent connections. References to to
separate connections to the ssme database or multi-database won't
work.
For example, it's tempting to open a second database using the
database open function, but this doesn't work:
>>> import ZODB.tests.util, transaction, persistent
>>> databases = {}
>>> db1 = ZODB.tests.util.DB(databases=databases, database_name='1')
>>> db2 = ZODB.tests.util.DB(databases=databases, database_name='2')
>>> tm = transaction.TransactionManager()
>>> conn1 = db1.open(transaction_manager=tm)
>>> p1 = MyClass()
>>> conn1.root()['p'] = p1
>>> tm.commit()
>>> conn2 = db2.open(transaction_manager=tm)
>>> p2 = MyClass()
>>> conn2.root()['p'] = p2
>>> p2.p1 = p1
>>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
InvalidObjectReference: Attempt to store a reference to an object
from a separate onnection to the same database or multidatabase
>>> tm.abort()
Even without multi-databases, a common mistake is to mix objects in
different connections to the same database.
>>> conn2 = db1.open(transaction_manager=tm)
>>> p2 = MyClass()
>>> conn2.root()['p'] = p2
>>> p2.p1 = p1
>>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
InvalidObjectReference: Attempt to store a reference to an object
from a separate onnection to the same database or multidatabase
>>> tm.abort()
"""
def test_suite(): def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
doctest.DocFileSuite('../cross-database-references.txt', doctest.DocFileSuite('../cross-database-references.txt',
...@@ -34,6 +87,7 @@ def test_suite(): ...@@ -34,6 +87,7 @@ def test_suite():
doctest.DocFileSuite('../cross-database-references.txt', doctest.DocFileSuite('../cross-database-references.txt',
globs=dict(MyClass=MyClass_w_getnewargs), globs=dict(MyClass=MyClass_w_getnewargs),
), ),
doctest.DocTestSuite(),
)) ))
if __name__ == '__main__': if __name__ == '__main__':
......
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