Commit bd13e920 authored by Jim Fulton's avatar Jim Fulton

The databasesFrom... methods now support multiple databases.

Added an option to disallow cross-database references.
parent a996cb5b
...@@ -49,8 +49,15 @@ def databaseFromURL(url): ...@@ -49,8 +49,15 @@ def databaseFromURL(url):
config, handler = ZConfig.loadConfig(getDbSchema(), url) config, handler = ZConfig.loadConfig(getDbSchema(), url)
return databaseFromConfig(config.database) return databaseFromConfig(config.database)
def databaseFromConfig(section): def databaseFromConfig(database_factories):
return section.open() databases = {}
first = None
for factory in database_factories:
db = factory.open(databases)
if first is None:
first = db
return first
def storageFromString(s): def storageFromString(s):
return storageFromFile(StringIO(s)) return storageFromFile(StringIO(s))
...@@ -93,8 +100,16 @@ class ZODBDatabase(BaseConfig): ...@@ -93,8 +100,16 @@ class ZODBDatabase(BaseConfig):
section = self.config section = self.config
storage = section.storage.open() storage = section.storage.open()
options = {} options = {}
if section.pool_timeout is not None:
options['pool_timeout'] = section.pool_timeout def _option(name, oname=None):
v = getattr(section, name)
if v is not None:
if oname is None:
oname = name
options[oname] = v
_option('pool_timeout')
_option('allow_implicit_cross_references', 'xrefs')
try: try:
return ZODB.DB( return ZODB.DB(
...@@ -106,7 +121,7 @@ class ZODBDatabase(BaseConfig): ...@@ -106,7 +121,7 @@ class ZODBDatabase(BaseConfig):
historical_cache_size=section.historical_cache_size, historical_cache_size=section.historical_cache_size,
historical_cache_size_bytes=section.historical_cache_size_bytes, historical_cache_size_bytes=section.historical_cache_size_bytes,
historical_timeout=section.historical_timeout, historical_timeout=section.historical_timeout,
database_name=section.database_name, database_name=section.database_name or self.name or '',
databases=databases, databases=databases,
**options) **options)
except: except:
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<import package="ZODB"/> <import package="ZODB"/>
<section type="ZODB.database" name="*" attribute="database"/> <multisection type="ZODB.database" name="*" attribute="database"
required="yes" />
</schema> </schema>
...@@ -12,23 +12,26 @@ ...@@ -12,23 +12,26 @@
# #
############################################################################## ##############################################################################
import os import doctest
import transaction import tempfile
import unittest import unittest
import ZEO.ClientStorage
import transaction
import ZODB.config import ZODB.config
import ZODB.POSException from ZODB.POSException import ReadOnlyError
import ZODB.tests.util
from zope.testing import doctest
class ConfigTestBase(ZODB.tests.util.TestCase):
class ConfigTestBase(unittest.TestCase):
def _opendb(self, s): def _opendb(self, s):
return ZODB.config.databaseFromString(s) return ZODB.config.databaseFromString(s)
def tearDown(self):
if getattr(self, "storage", None) is not None:
self.storage.cleanup()
def _test(self, s): def _test(self, s):
db = self._opendb(s) db = self._opendb(s)
self.storage = db.storage self.storage = db._storage
# Do something with the database to make sure it works # Do something with the database to make sure it works
cn = db.open() cn = db.open()
rt = cn.root() rt = cn.root()
...@@ -56,26 +59,28 @@ class ZODBConfigTest(ConfigTestBase): ...@@ -56,26 +59,28 @@ class ZODBConfigTest(ConfigTestBase):
""") """)
def test_file_config1(self): def test_file_config1(self):
path = tempfile.mktemp()
self._test( self._test(
""" """
<zodb> <zodb>
<filestorage> <filestorage>
path Data.fs path %s
</filestorage> </filestorage>
</zodb> </zodb>
""") """ % path)
def test_file_config2(self): def test_file_config2(self):
path = tempfile.mktemp()
cfg = """ cfg = """
<zodb> <zodb>
<filestorage> <filestorage>
path Data.fs path %s
create false create false
read-only true read-only true
</filestorage> </filestorage>
</zodb> </zodb>
""" """ % path
self.assertRaises(ZODB.POSException.ReadOnlyError, self._test, cfg) self.assertRaises(ReadOnlyError, self._test, cfg)
def test_demo_config(self): def test_demo_config(self):
cfg = """ cfg = """
...@@ -110,54 +115,116 @@ class ZEOConfigTest(ConfigTestBase): ...@@ -110,54 +115,116 @@ class ZEOConfigTest(ConfigTestBase):
</zodb> </zodb>
""" """
config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg)) config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg))
self.assertEqual(config.database.config.storage.config.blob_dir, self.assertEqual(config.database[0].config.storage.config.blob_dir,
None) None)
self.assertRaises(ClientDisconnected, self._test, cfg) self.assertRaises(ClientDisconnected, self._test, cfg)
cfg = """ cfg = """
<zodb> <zodb>
<zeoclient> <zeoclient>
blob-dir blobs blob-dir /tmp
server localhost:56897 server localhost:56897
wait false wait false
</zeoclient> </zeoclient>
</zodb> </zodb>
""" """
config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg)) config, handle = ZConfig.loadConfigFile(getDbSchema(), StringIO(cfg))
self.assertEqual( self.assertEqual(config.database[0].config.storage.config.blob_dir,
os.path.abspath(config.database.config.storage.config.blob_dir), '/tmp')
os.path.abspath('blobs'))
self.assertRaises(ClientDisconnected, self._test, cfg) self.assertRaises(ClientDisconnected, self._test, cfg)
def db_connection_pool_timeout(): def database_xrefs_config():
r"""
>>> db = ZODB.config.databaseFromString(
... "<zodb>\n<mappingstorage>\n</mappingstorage>\n</zodb>\n")
>>> db.xrefs
True
>>> db = ZODB.config.databaseFromString(
... "<zodb>\nallow-implicit-cross-references true\n"
... "<mappingstorage>\n</mappingstorage>\n</zodb>\n")
>>> db.xrefs
True
>>> db = ZODB.config.databaseFromString(
... "<zodb>\nallow-implicit-cross-references false\n"
... "<mappingstorage>\n</mappingstorage>\n</zodb>\n")
>>> db.xrefs
False
""" """
Test that the database pool timeout option works:
def multi_atabases():
r"""If there are multiple codb sections -> multidatabase
>>> db = ZODB.config.databaseFromString(''' >>> db = ZODB.config.databaseFromString('''
... <zodb> ... <zodb>
... <mappingstorage/> ... <mappingstorage>
... </mappingstorage>
... </zodb>
... <zodb Foo>
... <mappingstorage>
... </mappingstorage>
... </zodb>
... <zodb>
... database-name Bar
... <mappingstorage>
... </mappingstorage>
... </zodb> ... </zodb>
... ''') ... ''')
>>> db.pool._timeout == 1<<31 >>> sorted(db.databases)
['', 'Bar', 'foo']
>>> db.database_name
''
>>> db.databases[db.database_name] is db
True
>>> db.databases['foo'] is not db
True
>>> db.databases['Bar'] is not db
True
>>> db.databases['Bar'] is not db.databases['foo']
True True
>>> db = ZODB.config.databaseFromString(''' Can't have repeats:
>>> ZODB.config.databaseFromString('''
... <zodb 1>
... <mappingstorage>
... </mappingstorage>
... </zodb>
... <zodb 1>
... <mappingstorage>
... </mappingstorage>
... </zodb>
... <zodb 1>
... <mappingstorage>
... </mappingstorage>
... </zodb>
... ''') # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ConfigurationSyntaxError:
section names must not be re-used within the same container:'1' (line 9)
>>> ZODB.config.databaseFromString('''
... <zodb> ... <zodb>
... pool-timeout 600 ... <mappingstorage>
... <mappingstorage/> ... </mappingstorage>
... </zodb> ... </zodb>
... ''') ... <zodb>
>>> db.pool._timeout == 600 ... <mappingstorage>
True ... </mappingstorage>
... </zodb>
... ''') # doctest: +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: database_name '' already in databases
""" """
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite())
suite.addTest(unittest.makeSuite(ZODBConfigTest)) suite.addTest(unittest.makeSuite(ZODBConfigTest))
suite.addTest(unittest.makeSuite(ZEOConfigTest)) suite.addTest(unittest.makeSuite(ZEOConfigTest))
suite.addTest(doctest.DocTestSuite())
return suite return suite
......
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