summaryrefslogtreecommitdiff
path: root/src/mailman/database/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/database/base.py')
-rw-r--r--src/mailman/database/base.py143
1 files changed, 18 insertions, 125 deletions
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index cbf88a4ff..2b86899bc 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -19,49 +19,39 @@ from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
- 'StormBaseDatabase',
+ 'SABaseDatabase',
]
-import os
-import sys
import logging
-from lazr.config import as_boolean
-from pkg_resources import resource_listdir, resource_string
-from storm.cache import GenerationalCache
-from storm.locals import create_database, Store
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
from zope.interface import implementer
from mailman.config import config
from mailman.interfaces.database import IDatabase
-from mailman.model.version import Version
from mailman.utilities.string import expand
-log = logging.getLogger('mailman.config')
+log = logging.getLogger('mailman.database')
NL = '\n'
@implementer(IDatabase)
-class StormBaseDatabase:
- """The database base class for use with the Storm ORM.
+class SABaseDatabase:
+ """The database base class for use with SQLAlchemy.
- Use this as a base class for your DB-specific derived classes.
+ Use this as a base class for your DB-Specific derived classes.
"""
-
- # Tag used to distinguish the database being used. Override this in base
- # classes.
- TAG = ''
-
def __init__(self):
self.url = None
self.store = None
def begin(self):
"""See `IDatabase`."""
- # Storm takes care of this for us.
+ # SQLAlchemy does this for us.
pass
def commit(self):
@@ -72,16 +62,6 @@ class StormBaseDatabase:
"""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 _pre_reset(self, store):
"""Clean up method for testing.
@@ -113,6 +93,7 @@ class StormBaseDatabase:
"""See `IDatabase`."""
# Calculate the engine url.
url = expand(config.database.url, config.paths)
+ self._prepare(url)
log.debug('Database url: %s', url)
# XXX By design of SQLite, database file creation does not honor
# umask. See their ticket #1193:
@@ -129,101 +110,13 @@ class StormBaseDatabase:
# engines, and yes, we could have chmod'd the file after the fact, but
# half dozen and all...
self.url = url
- self._prepare(url)
- database = create_database(url)
- store = Store(database, GenerationalCache())
- database.DEBUG = (as_boolean(config.database.debug)
- if debug is None else debug)
- self.store = store
- store.commit()
-
- def load_migrations(self, until=None):
- """Load schema migrations.
-
- :param until: Load only the migrations up to the specified timestamp.
- With default value of None, load all migrations.
- :type until: string
- """
- migrations_path = config.database.migrations_path
- if '.' in migrations_path:
- parent, dot, child = migrations_path.rpartition('.')
- else:
- parent = migrations_path
- child = ''
- # If the database does not yet exist, load the base schema.
- filenames = sorted(resource_listdir(parent, child))
- # Find out which schema migrations have already been loaded.
- if self._database_exists(self.store):
- versions = set(version.version for version in
- self.store.find(Version, component='schema'))
- else:
- versions = set()
- for filename in filenames:
- module_fn, extension = os.path.splitext(filename)
- if extension != '.py':
- continue
- parts = module_fn.split('_')
- if len(parts) < 2:
- continue
- version = parts[1].strip()
- if len(version) == 0:
- # Not a schema migration file.
- continue
- if version in versions:
- log.debug('already migrated to %s', version)
- continue
- if until is not None and version > until:
- # We're done.
- break
- module_path = migrations_path + '.' + module_fn
- __import__(module_path)
- upgrade = getattr(sys.modules[module_path], 'upgrade', None)
- if upgrade is None:
- continue
- log.debug('migrating db to %s: %s', version, module_path)
- upgrade(self, self.store, version, module_path)
- self.commit()
-
- def load_sql(self, store, sql):
- """Load the given SQL into the store.
-
- :param store: The Storm store to load the schema into.
- :type store: storm.locals.Store`
- :param sql: The possibly multi-line SQL to load.
- :type sql: string
- """
- # Discard all blank and comment lines.
- lines = (line for line in sql.splitlines()
- if line.strip() != '' and line.strip()[:2] != '--')
- sql = NL.join(lines)
- for statement in sql.split(';'):
- if statement.strip() != '':
- store.execute(statement + ';')
-
- def load_schema(self, store, version, filename, module_path):
- """Load the schema from a file.
-
- This is a helper method for migration classes to call.
-
- :param store: The Storm store to load the schema into.
- :type store: storm.locals.Store`
- :param version: The schema version identifier of the form
- YYYYMMDDHHMMSS.
- :type version: string
- :param filename: The file name containing the schema to load. Pass
- `None` if there is no schema file to load.
- :type filename: string
- :param module_path: The fully qualified Python module path to the
- migration module being loaded. This is used to record information
- for use by the test suite.
- :type module_path: string
- """
- if filename is not None:
- contents = resource_string('mailman.database.schema', filename)
- self.load_sql(store, contents)
- # Add a marker that indicates the migration version being applied.
- store.add(Version(component='schema', version=version))
+ self.engine = create_engine(url)
+ session = sessionmaker(bind=self.engine)
+ self.store = session()
+ self.store.commit()
- @staticmethod
- def _make_temporary():
- raise NotImplementedError
+ # XXX BAW Why doesn't model.py _reset() do this?
+ def destroy(self):
+ """Drop all database tables"""
+ from mailman.database.model import Model
+ Model.metadata.drop_all(self.engine)