26
import Mailman.Version
27
28
from locknix.lockfile import Lock
28
from storm.properties import PropertyPublisherMeta
29
from storm.locals import create_database, Store
30
from string import Template
31
from urlparse import urlparse
29
32
from zope.interface import implements
31
from Mailman.interfaces import IDatabase
34
from Mailman.Errors import SchemaVersionMismatchError
35
from Mailman.configuration import config
32
36
from Mailman.database.listmanager import ListManager
37
from Mailman.database.messagestore import MessageStore
38
from Mailman.database.pending import Pendings
39
from Mailman.database.requests import Requests
33
40
from Mailman.database.usermanager import UserManager
34
from Mailman.database.messagestore import MessageStore
41
from Mailman.database.version import Version
42
from Mailman.interfaces import IDatabase
49
57
def initialize(self, debug=None):
50
# Avoid circular imports.
51
from Mailman.configuration import config
52
from Mailman.database import model
53
from Mailman.database.model import Pendings
54
from Mailman.database.model import Requests
55
58
# Serialize this so we don't get multiple processes trying to create
56
59
# the database at the same time.
57
60
with Lock(os.path.join(config.LOCK_DIR, 'dbcreate.lck')):
58
self.store = model.initialize(debug)
59
62
self.list_manager = ListManager()
60
63
self.user_manager = UserManager()
61
64
self.message_store = MessageStore()
62
65
self.pendings = Pendings()
63
66
self.requests = Requests()
68
def _create(self, debug):
69
# Calculate the engine url.
70
url = Template(config.DEFAULT_DATABASE_URL).safe_substitute(
72
# XXX By design of SQLite, database file creation does not honor
73
# umask. See their ticket #1193:
74
# http://www.sqlite.org/cvstrac/tktview?tn=1193,31
76
# This sucks for us because the mailman.db file /must/ be group
77
# writable, however even though we guarantee our umask is 002 here, it
78
# still gets created without the necessary g+w permission, due to
79
# SQLite's policy. This should only affect SQLite engines because its
80
# the only one that creates a little file on the local file system.
81
# This kludges around their bug by "touch"ing the database file before
82
# SQLite has any chance to create it, thus honoring the umask and
83
# ensuring the right permissions. We only try to do this for SQLite
84
# engines, and yes, we could have chmod'd the file after the fact, but
85
# half dozen and all...
87
database = create_database(url)
88
store = Store(database)
89
database.DEBUG = (config.DEFAULT_DATABASE_ECHO
90
if debug is None else debug)
91
# Storm does not currently have schema creation. This is not an ideal
92
# way to handle creating the database, but it's cheap and easy for
94
import Mailman.database
95
schema_file = os.path.join(
96
os.path.dirname(Mailman.database.__file__),
98
with open(schema_file) as fp:
100
for statement in sql.split(';'):
101
store.execute(statement + ';')
102
# Validate schema version.
103
v = store.find(Version, component=u'schema').one()
105
# Database has not yet been initialized
106
v = Version(component=u'schema',
107
version=Mailman.Version.DATABASE_SCHEMA_VERSION)
109
elif v.version <> Mailman.Version.DATABASE_SCHEMA_VERSION:
111
raise SchemaVersionMismatchError(v.version)
66
for model_class in _class_registry:
67
for row in self.store.find(model_class):
68
self.store.remove(row)
115
from Mailman.database.model import ModelMeta
116
ModelMeta._reset(self.store)
72
_class_registry = set()
75
class ModelMeta(PropertyPublisherMeta):
76
"""Do more magic on table classes."""
78
def __init__(self, name, bases, dict):
79
# Before we let the base class do it's thing, force an __storm_table__
80
# property to enforce our table naming convention.
81
self.__storm_table__ = name.lower()
82
super(ModelMeta, self).__init__(name, bases, dict)
83
# Register the model class so that it can be more easily cleared.
84
# This is required by the test framework.
87
_class_registry.add(self)
91
"""Like Storm's `Storm` subclass, but with a bit extra."""
92
__metaclass__ = ModelMeta
121
parts = urlparse(url)
122
if parts.scheme <> 'sqlite':
124
path = os.path.normpath(parts.path)
125
fd = os.open(path, os.O_WRONLY | os.O_NONBLOCK | os.O_CREAT, 0666)