summaryrefslogtreecommitdiff
path: root/Mailman/database/dbcontext.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/database/dbcontext.py')
-rw-r--r--Mailman/database/dbcontext.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/Mailman/database/dbcontext.py b/Mailman/database/dbcontext.py
new file mode 100644
index 000000000..b812c9b77
--- /dev/null
+++ b/Mailman/database/dbcontext.py
@@ -0,0 +1,144 @@
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+import weakref
+
+from sqlalchemy import *
+from string import Template
+
+from Mailman import Version
+from Mailman.configuration import config
+from Mailman.database import address
+from Mailman.database import listdata
+from Mailman.database import version
+from Mailman.database.txnsupport import txn
+
+
+
+class MlistRef(weakref.ref):
+ def __init__(self, mlist, callback):
+ super(MlistRef, self).__init__(mlist, callback)
+ self.fqdn_listname = mlist.fqdn_listname
+
+
+
+class DBContext(object):
+ def __init__(self):
+ self.tables = {}
+ self.metadata = None
+ self.session = None
+ # Special transaction used only for MailList.Lock() .Save() and
+ # .Unlock() interface.
+ self._mlist_txns = {}
+
+ def connect(self):
+ # Calculate the engine url
+ url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(
+ config.paths)
+ self.metadata = BoundMetaData(url)
+ self.metadata.engine.echo = config.SQLALCHEMY_ECHO
+ # Create all the table objects, and then let SA conditionally create
+ # them if they don't yet exist.
+ version_table = None
+ for module in (address, listdata, version):
+ table = module.make_table(self.metadata)
+ self.tables[table.name] = table
+ if module is version:
+ version_table = table
+ self.metadata.create_all()
+ # Validate schema version, updating if necessary (XXX)
+ from Mailman.interact import interact
+ r = version_table.select(version_table.c.component=='schema').execute()
+ row = r.fetchone()
+ if row is None:
+ # Database has not yet been initialized
+ version_table.insert().execute(
+ component='schema',
+ version=Version.DATABASE_SCHEMA_VERSION)
+ elif row.version <> Version.DATABASE_SCHEMA_VERSION:
+ # XXX Update schema
+ raise SchemaVersionMismatchError(row.version)
+ self.session = create_session()
+
+ # Cooperative method for use with @txn decorator
+ def _withtxn(self, meth, *args, **kws):
+ try:
+ txn = self.session.create_transaction()
+ rtn = meth(*args, **kws)
+ except:
+ txn.rollback()
+ raise
+ else:
+ txn.commit()
+ return rtn
+
+ def _unlock_mref(self, mref):
+ txn = self._mlist_txns.pop(mref.fqdn_listname, None)
+ if txn is not None:
+ txn.rollback()
+
+ # Higher level interface
+ def api_lock(self, mlist):
+ # Don't try to re-lock a list
+ if mlist.fqdn_listname in self._mlist_txns:
+ return
+ txn = self.session.create_transaction()
+ mref = MlistRef(mlist, self._unlock_mref)
+ self._mlist_txns[mlist.fqdn_listname] = txn
+
+ def api_unlock(self, mlist):
+ txn = self._mlist_txns.pop(mlist.fqdn_listname, None)
+ if txn is not None:
+ txn.rollback()
+
+ def api_save(self, mlist):
+ # When dealing with MailLists, .Save() will always be followed by
+ # .Unlock(). However lists can also be unlocked without saving. But
+ # if it's been locked it will always be unlocked. So the rollback in
+ # unlock will essentially be no-op'd if we've already saved the list.
+ txn = self._mlist_txns.pop(mlist.fqdn_listname, None)
+ if txn is not None:
+ txn.commit()
+
+ @txn
+ def api_add_list(self, mlist):
+ self.session.save(mlist)
+
+ @txn
+ def api_remove_list(self, mlist):
+ self.session.delete(mlist)
+
+ @txn
+ def api_find_list(self, listname, hostname):
+ from Mailman.MailList import MailList
+ q = self.session.query(MailList)
+ mlists = q.select_by(list_name=listname, host_name=hostname)
+ assert len(mlists) <= 1, 'Duplicate mailing lists!'
+ if mlists:
+ return mlists[0]
+ return None
+
+ @txn
+ def api_get_list_names(self):
+ table = self.tables['Listdata']
+ results = table.select().execute()
+ return [(row[table.c.list_name], row[table.c.host_name])
+ for row in results.fetchall()]
+
+
+
+dbcontext = DBContext()