diff options
| author | Barry Warsaw | 2011-10-22 17:56:20 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2011-10-22 17:56:20 -0400 |
| commit | 3ecf213a31e198f7dee11abb66091842d3c11241 (patch) | |
| tree | f4d2db82cbad5afd15670dfd2199368dafce9275 /src/mailman/database/base.py | |
| parent | 63b338e18c6cf07a3c46a8e9db436c9c10654330 (diff) | |
| parent | e24d8cdc4f9006f8cdfd84e9610cb6416d5961b9 (diff) | |
| download | mailman-3ecf213a31e198f7dee11abb66091842d3c11241.tar.gz mailman-3ecf213a31e198f7dee11abb66091842d3c11241.tar.zst mailman-3ecf213a31e198f7dee11abb66091842d3c11241.zip | |
Diffstat (limited to 'src/mailman/database/base.py')
| -rw-r--r-- | src/mailman/database/base.py | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py new file mode 100644 index 000000000..1c41a2fba --- /dev/null +++ b/src/mailman/database/base.py @@ -0,0 +1,159 @@ +# Copyright (C) 2006-2011 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'StormBaseDatabase', + ] + + +import os +import logging + +from flufl.lock import Lock +from lazr.config import as_boolean +from storm.cache import GenerationalCache +from storm.locals import create_database, Store +from urlparse import urlparse +from zope.interface import implements + +import mailman.version + +from mailman.config import config +from mailman.interfaces.database import IDatabase, SchemaVersionMismatchError +from mailman.model.version import Version +from mailman.utilities.string import expand + +log = logging.getLogger('mailman.config') + + + +class StormBaseDatabase: + """The database base class for use with the Storm ORM. + + Use this as a base class for your DB-specific derived classes. + """ + + implements(IDatabase) + + def __init__(self): + self.url = None + self.store = None + + def initialize(self, debug=None): + """See `IDatabase`.""" + # 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._create(debug) + + def begin(self): + """See `IDatabase`.""" + # Storm takes care of this for us. + pass + + def commit(self): + """See `IDatabase`.""" + self.store.commit() + + def abort(self): + """See `IDatabase`.""" + self.store.rollback() + + def _database_exists(self): + """Return True if the database exists and is initialized. + + Return False when Mailman needs to create and initialize the + underlying database schema. + + Base classes *must* override this. + """ + raise NotImplementedError + + def _get_schema(self): + """Return the database schema as a string. + + This will be loaded into the database when it is first created. + + Base classes *must* override this. + """ + raise NotImplementedError + + def _create(self, debug): + # Calculate the engine url. + url = expand(config.database.url, config.paths) + log.debug('Database url: %s', url) + # 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... + self.url = url + touch(url) + database = create_database(url) + store = Store(database, GenerationalCache()) + database.DEBUG = (as_boolean(config.database.debug) + if debug is None else debug) + # Check the master / schema database to see if the version table + # exists. If so, then we assume the database schema is correctly + # initialized. Storm does not currently provide schema creation. + if not self._database_exists(store): + # Initialize the database. + for statement in self._get_schema().split(';'): + if statement.strip() != '': + store.execute(statement + ';') + # Validate schema version. + v = store.find(Version, component='schema').one() + if not v: + # Database has not yet been initialized + v = Version(component='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 + store.commit() + + def _reset(self): + """See `IDatabase`.""" + from mailman.database.model import ModelMeta + self.store.rollback() + ModelMeta._reset(self.store) + + + +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) |
