summaryrefslogtreecommitdiff
path: root/Mailman/database/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/database/__init__.py')
-rw-r--r--Mailman/database/__init__.py102
1 files changed, 69 insertions, 33 deletions
diff --git a/Mailman/database/__init__.py b/Mailman/database/__init__.py
index 78b118be6..d332254c6 100644
--- a/Mailman/database/__init__.py
+++ b/Mailman/database/__init__.py
@@ -23,15 +23,23 @@ __all__ = [
]
import os
+import Mailman.Version
from locknix.lockfile import Lock
-from storm.properties import PropertyPublisherMeta
+from storm.locals import create_database, Store
+from string import Template
+from urlparse import urlparse
from zope.interface import implements
-from Mailman.interfaces import IDatabase
+from Mailman.Errors import SchemaVersionMismatchError
+from Mailman.configuration import config
from Mailman.database.listmanager import ListManager
-from Mailman.database.usermanager import UserManager
from Mailman.database.messagestore import MessageStore
+from Mailman.database.pending import Pendings
+from Mailman.database.requests import Requests
+from Mailman.database.usermanager import UserManager
+from Mailman.database.version import Version
+from Mailman.interfaces import IDatabase
@@ -47,46 +55,74 @@ class StockDatabase:
self._store = None
def initialize(self, debug=None):
- # Avoid circular imports.
- from Mailman.configuration import config
- from Mailman.database import model
- from Mailman.database.model import Pendings
- from Mailman.database.model import Requests
# Serialize this so we don't get multiple processes trying to create
# the database at the same time.
with Lock(os.path.join(config.LOCK_DIR, 'dbcreate.lck')):
- self.store = model.initialize(debug)
+ self._create(debug)
self.list_manager = ListManager()
self.user_manager = UserManager()
self.message_store = MessageStore()
self.pendings = Pendings()
self.requests = Requests()
+ def _create(self, debug):
+ # Calculate the engine url.
+ url = Template(config.DEFAULT_DATABASE_URL).safe_substitute(
+ config.paths)
+ # XXX By design of SQLite, database file creation does not honor
+ # umask. See their ticket #1193:
+ # http://www.sqlite.org/cvstrac/tktview?tn=1193,31
+ #
+ # This sucks for us because the mailman.db file /must/ be group
+ # writable, however even though we guarantee our umask is 002 here, it
+ # still gets created without the necessary g+w permission, due to
+ # SQLite's policy. This should only affect SQLite engines because its
+ # the only one that creates a little file on the local file system.
+ # This kludges around their bug by "touch"ing the database file before
+ # SQLite has any chance to create it, thus honoring the umask and
+ # ensuring the right permissions. We only try to do this for SQLite
+ # engines, and yes, we could have chmod'd the file after the fact, but
+ # half dozen and all...
+ touch(url)
+ database = create_database(url)
+ store = Store(database)
+ database.DEBUG = (config.DEFAULT_DATABASE_ECHO
+ if debug is None else debug)
+ # Storm does not currently have schema creation. This is not an ideal
+ # way to handle creating the database, but it's cheap and easy for
+ # now.
+ import Mailman.database
+ schema_file = os.path.join(
+ os.path.dirname(Mailman.database.__file__),
+ 'mailman.sql')
+ with open(schema_file) as fp:
+ sql = fp.read()
+ for statement in sql.split(';'):
+ store.execute(statement + ';')
+ # Validate schema version.
+ v = store.find(Version, component=u'schema').one()
+ if not v:
+ # Database has not yet been initialized
+ v = Version(component=u'schema',
+ version=Mailman.Version.DATABASE_SCHEMA_VERSION)
+ store.add(v)
+ elif v.version <> Mailman.Version.DATABASE_SCHEMA_VERSION:
+ # XXX Update schema
+ raise SchemaVersionMismatchError(v.version)
+ self.store = store
+
def _reset(self):
- for model_class in _class_registry:
- for row in self.store.find(model_class):
- self.store.remove(row)
+ from Mailman.database.model import ModelMeta
+ ModelMeta._reset(self.store)
-_class_registry = set()
-
-
-class ModelMeta(PropertyPublisherMeta):
- """Do more magic on table classes."""
-
- def __init__(self, name, bases, dict):
- # Before we let the base class do it's thing, force an __storm_table__
- # property to enforce our table naming convention.
- self.__storm_table__ = name.lower()
- super(ModelMeta, self).__init__(name, bases, dict)
- # Register the model class so that it can be more easily cleared.
- # This is required by the test framework.
- if name == 'Model':
- return
- _class_registry.add(self)
-
-
-class Model(object):
- """Like Storm's `Storm` subclass, but with a bit extra."""
- __metaclass__ = ModelMeta
+def touch(url):
+ parts = urlparse(url)
+ if parts.scheme <> 'sqlite':
+ return
+ path = os.path.normpath(parts.path)
+ fd = os.open(path, os.O_WRONLY | os.O_NONBLOCK | os.O_CREAT, 0666)
+ # Ignore errors
+ if fd > 0:
+ os.close(fd)