diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/config/schema.cfg | 2 | ||||
| -rw-r--r-- | src/mailman/database/base.py (renamed from src/mailman/database/stock.py) | 48 | ||||
| -rw-r--r-- | src/mailman/database/postgresql.py | 47 | ||||
| -rw-r--r-- | src/mailman/database/sql/__init__.py | 0 | ||||
| -rw-r--r-- | src/mailman/database/sql/postgres.sql | 329 | ||||
| -rw-r--r-- | src/mailman/database/sql/sqlite.sql (renamed from src/mailman/database/mailman.sql) | 0 | ||||
| -rw-r--r-- | src/mailman/database/sqlite.py | 46 |
7 files changed, 456 insertions, 16 deletions
diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg index 40c4756c2..8f0c863fc 100644 --- a/src/mailman/config/schema.cfg +++ b/src/mailman/config/schema.cfg @@ -191,7 +191,7 @@ sleep_time: 1s [database] # The class implementing the IDatabase. -class: mailman.database.stock.StockDatabase +class: mailman.database.sqlite.SQLiteDatabase # Use this to set the Storm database engine URL. You generally have one # primary database connection for all of Mailman. List data and most rosters diff --git a/src/mailman/database/stock.py b/src/mailman/database/base.py index e69fe9c7c..1c41a2fba 100644 --- a/src/mailman/database/stock.py +++ b/src/mailman/database/base.py @@ -19,15 +19,15 @@ from __future__ import absolute_import, unicode_literals __metaclass__ = type __all__ = [ - 'StockDatabase', + 'StormBaseDatabase', ] + import os import logging from flufl.lock import Lock from lazr.config import as_boolean -from pkg_resources import resource_string from storm.cache import GenerationalCache from storm.locals import create_database, Store from urlparse import urlparse @@ -44,8 +44,11 @@ log = logging.getLogger('mailman.config') -class StockDatabase: - """The standard database, using Storm on top of SQLite.""" +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) @@ -73,6 +76,25 @@ class StockDatabase: """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) @@ -97,18 +119,14 @@ class StockDatabase: store = Store(database, GenerationalCache()) database.DEBUG = (as_boolean(config.database.debug) if debug is None else debug) - # Check the sqlite master database to see if the version file exists. - # If so, then we assume the database schema is correctly initialized. - # 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. - table_names = [item[0] for item in - store.execute('select tbl_name from sqlite_master;')] - if 'version' not in table_names: + # 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. - sql = resource_string('mailman.database', 'mailman.sql') - for statement in sql.split(';'): - store.execute(statement + ';') + 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: diff --git a/src/mailman/database/postgresql.py b/src/mailman/database/postgresql.py new file mode 100644 index 000000000..b59165243 --- /dev/null +++ b/src/mailman/database/postgresql.py @@ -0,0 +1,47 @@ +# Copyright (C) 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/>. + +"""PostgreSQL database support.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'PostgreSQLDatabase', + ] + + +from pkg_resources import resource_string + +from mailman.database.base import StormBaseDatabase + + + +class PostgreSQLDatabase(StormBaseDatabase): + """Database class for PostgreSQL.""" + + def _database_exists(self, store): + """See `BaseDatabase`.""" + table_query = ('SELECT table_name FROM information_schema.tables ' + "WHERE table_schema = 'public'") + table_names = set(item[0] for item in + store.execute(table_query)) + return 'version' in table_names + + def _get_schema(self): + """See `BaseDatabase`.""" + return resource_string('mailman.database.sql', 'postgres.sql') diff --git a/src/mailman/database/sql/__init__.py b/src/mailman/database/sql/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/mailman/database/sql/__init__.py diff --git a/src/mailman/database/sql/postgres.sql b/src/mailman/database/sql/postgres.sql new file mode 100644 index 000000000..1100b73c9 --- /dev/null +++ b/src/mailman/database/sql/postgres.sql @@ -0,0 +1,329 @@ +CREATE TABLE mailinglist ( + id SERIAL NOT NULL, + -- List identity + list_name TEXT, + mail_host TEXT, + list_id TEXT, + include_list_post_header BOOLEAN, + include_rfc2369_headers BOOLEAN, + -- Attributes not directly modifiable via the web u/i + created_at TIMESTAMP, + admin_member_chunksize INTEGER, + next_request_id INTEGER, + next_digest_number INTEGER, + digest_last_sent_at TIMESTAMP, + volume INTEGER, + last_post_at TIMESTAMP, + accept_these_nonmembers BYTEA, + acceptable_aliases_id INTEGER, + admin_immed_notify BOOLEAN, + admin_notify_mchanges BOOLEAN, + administrivia BOOLEAN, + advertised BOOLEAN, + anonymous_list BOOLEAN, + archive BOOLEAN, + archive_private BOOLEAN, + archive_volume_frequency INTEGER, + -- Automatic responses. + autorespond_owner INTEGER, + autoresponse_owner_text TEXT, + autorespond_postings INTEGER, + autoresponse_postings_text TEXT, + autorespond_requests INTEGER, + autoresponse_request_text TEXT, + autoresponse_grace_period TEXT, + -- Bounces. + forward_unrecognized_bounces_to INTEGER, + process_bounces BOOLEAN, + bounce_info_stale_after TEXT, + bounce_matching_headers TEXT, + bounce_notify_owner_on_disable BOOLEAN, + bounce_notify_owner_on_removal BOOLEAN, + bounce_score_threshold INTEGER, + bounce_you_are_disabled_warnings INTEGER, + bounce_you_are_disabled_warnings_interval TEXT, + -- Content filtering. + filter_content BOOLEAN, + collapse_alternatives BOOLEAN, + convert_html_to_plaintext BOOLEAN, + default_member_action INTEGER, + default_nonmember_action INTEGER, + description TEXT, + digest_footer TEXT, + digest_header TEXT, + digest_is_default BOOLEAN, + digest_send_periodic BOOLEAN, + digest_size_threshold INTEGER, + digest_volume_frequency INTEGER, + digestable BOOLEAN, + discard_these_nonmembers BYTEA, + emergency BOOLEAN, + encode_ascii_prefixes BOOLEAN, + first_strip_reply_to BOOLEAN, + forward_auto_discards BOOLEAN, + gateway_to_mail BOOLEAN, + gateway_to_news BOOLEAN, + generic_nonmember_action INTEGER, + goodbye_msg TEXT, + header_matches BYTEA, + hold_these_nonmembers BYTEA, + info TEXT, + linked_newsgroup TEXT, + max_days_to_hold INTEGER, + max_message_size INTEGER, + max_num_recipients INTEGER, + member_moderation_notice TEXT, + mime_is_default_digest BOOLEAN, + moderator_password TEXT, + msg_footer TEXT, + msg_header TEXT, + new_member_options INTEGER, + news_moderation INTEGER, + news_prefix_subject_too BOOLEAN, + nntp_host TEXT, + nondigestable BOOLEAN, + nonmember_rejection_notice TEXT, + obscure_addresses BOOLEAN, + personalize INTEGER, + pipeline TEXT, + post_id INTEGER, + preferred_language TEXT, + private_roster BOOLEAN, + real_name TEXT, + reject_these_nonmembers BYTEA, + reply_goes_to_list INTEGER, + reply_to_address TEXT, + require_explicit_destination BOOLEAN, + respond_to_post_requests BOOLEAN, + scrub_nondigest BOOLEAN, + send_goodbye_msg BOOLEAN, + send_reminders BOOLEAN, + send_welcome_msg BOOLEAN, + start_chain TEXT, + subject_prefix TEXT, + subscribe_auto_approval BYTEA, + subscribe_policy INTEGER, + topics BYTEA, + topics_bodylines_limit INTEGER, + topics_enabled BOOLEAN, + unsubscribe_policy INTEGER, + welcome_msg TEXT, + moderation_callback TEXT, + PRIMARY KEY (id) + ); + +CREATE TABLE _request ( + id SERIAL NOT NULL, + "key" TEXT, + request_type INTEGER, + data_hash BYTEA, + mailing_list_id INTEGER, + PRIMARY KEY (id), + CONSTRAINT _request_mailing_list_id_fk + FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id) + ); + +CREATE TABLE acceptablealias ( + id SERIAL NOT NULL, + "alias" TEXT NOT NULL, + mailing_list_id INTEGER NOT NULL, + PRIMARY KEY (id), + CONSTRAINT acceptablealias_mailing_list_id_fk + FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id) + ); +CREATE INDEX ix_acceptablealias_mailing_list_id + ON acceptablealias (mailing_list_id); +CREATE INDEX ix_acceptablealias_alias ON acceptablealias ("alias"); + +CREATE TABLE preferences ( + id SERIAL NOT NULL, + acknowledge_posts BOOLEAN, + hide_address BOOLEAN, + preferred_language TEXT, + receive_list_copy BOOLEAN, + receive_own_postings BOOLEAN, + delivery_mode INTEGER, + delivery_status INTEGER, + PRIMARY KEY (id) + ); + +CREATE TABLE address ( + id SERIAL NOT NULL, + email TEXT, + _original TEXT, + real_name TEXT, + verified_on TIMESTAMP, + registered_on TIMESTAMP, + user_id INTEGER, + preferences_id INTEGER, + PRIMARY KEY (id), + CONSTRAINT address_preferences_id_fk + FOREIGN KEY (preferences_id) REFERENCES preferences (id) + ); + +CREATE TABLE "user" ( + id SERIAL NOT NULL, + real_name TEXT, + password BYTEA, + _user_id UUID, + _created_on TIMESTAMP, + _preferred_address_id INTEGER, + preferences_id INTEGER, + PRIMARY KEY (id), + CONSTRAINT user_preferences_id_fk + FOREIGN KEY (preferences_id) REFERENCES preferences (id), + CONSTRAINT _preferred_address_id_fk + FOREIGN KEY (_preferred_address_id) REFERENCES address (id) + ); +CREATE INDEX ix_user_user_id ON "user" (_user_id); + +-- since user and address have circular foreign key refs, the +-- constraint on the address table has to be added after +-- the user table is created +ALTER TABLE address ADD + CONSTRAINT address_user_id_fk + FOREIGN KEY (user_id) REFERENCES "user" (id); + +CREATE TABLE autoresponserecord ( + id SERIAL NOT NULL, + address_id INTEGER, + mailing_list_id INTEGER, + response_type INTEGER, + date_sent TIMESTAMP, + PRIMARY KEY (id), + CONSTRAINT autoresponserecord_address_id_fk + FOREIGN KEY (address_id) REFERENCES address (id) + -- TODO: figure why this FK is broken + -- , + -- CONSTRAINT autoresponserecord_mailing_list_id + -- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id) + ); +CREATE INDEX ix_autoresponserecord_address_id + ON autoresponserecord (address_id); +CREATE INDEX ix_autoresponserecord_mailing_list_id + ON autoresponserecord (mailing_list_id); + +CREATE TABLE bounceevent ( + id SERIAL NOT NULL, + list_name TEXT, + email TEXT, + "timestamp" TIMESTAMP, + message_id TEXT, + context INTEGER, + processed BOOLEAN, + PRIMARY KEY (id) + ); + +CREATE TABLE contentfilter ( + id SERIAL NOT NULL, + mailing_list_id INTEGER, + filter_pattern TEXT, + filter_type INTEGER, + PRIMARY KEY (id), + CONSTRAINT contentfilter_mailing_list_id + FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id) + ); +CREATE INDEX ix_contentfilter_mailing_list_id + ON contentfilter (mailing_list_id); + +CREATE TABLE domain ( + id SERIAL NOT NULL, + mail_host TEXT, + base_url TEXT, + description TEXT, + contact_address TEXT, + PRIMARY KEY (id) + ); + +CREATE TABLE language ( + id SERIAL NOT NULL, + code TEXT, + PRIMARY KEY (id) + ); + +CREATE TABLE member ( + id SERIAL NOT NULL, + _member_id UUID, + role INTEGER, + mailing_list TEXT, + moderation_action INTEGER, + address_id INTEGER, + preferences_id INTEGER, + user_id INTEGER, + PRIMARY KEY (id), + CONSTRAINT member_address_id_fk + FOREIGN KEY (address_id) REFERENCES address (id), + -- TODO: figure out where this is violated + -- CONSTRAINT member_preferences_id_fk + -- FOREIGN KEY (preferences_id) REFERENCES preferences (id), + CONSTRAINT member_user_id_fk + FOREIGN KEY (user_id) REFERENCES "user" (id) + ); +CREATE INDEX ix_member__member_id ON member (_member_id); +CREATE INDEX ix_member_address_id ON member (address_id); +CREATE INDEX ix_member_preferences_id ON member (preferences_id); + +CREATE TABLE message ( + id SERIAL NOT NULL, + message_id_hash BYTEA, + path BYTEA, + message_id TEXT, + PRIMARY KEY (id) + ); + +CREATE TABLE onelastdigest ( + id SERIAL NOT NULL, + mailing_list_id INTEGER, + address_id INTEGER, + delivery_mode INTEGER, + PRIMARY KEY (id), + CONSTRAINT onelastdigest_mailing_list_id_fk + FOREIGN KEY (mailing_list_id) REFERENCES mailinglist(id), + CONSTRAINT onelastdigest_address_id_fk + FOREIGN KEY (address_id) REFERENCES address(id) + ); + +CREATE TABLE pended ( + id SERIAL NOT NULL, + token BYTEA, + expiration_date TIMESTAMP, + PRIMARY KEY (id) + ); + +CREATE TABLE pendedkeyvalue ( + id SERIAL NOT NULL, + "key" TEXT, + value TEXT, + pended_id INTEGER, + PRIMARY KEY (id), + CONSTRAINT pendedkeyvalue_pended_id_fk + FOREIGN KEY (pended_id) REFERENCES pended (id) + ); + +CREATE TABLE version ( + id SERIAL NOT NULL, + component TEXT, + version INTEGER, + PRIMARY KEY (id) + ); + +CREATE INDEX ix__request_mailing_list_id ON _request (mailing_list_id); +CREATE INDEX ix_address_preferences_id ON address (preferences_id); +CREATE INDEX ix_address_user_id ON address (user_id); +CREATE INDEX ix_pendedkeyvalue_pended_id ON pendedkeyvalue (pended_id); +CREATE INDEX ix_user_preferences_id ON "user" (preferences_id); + +CREATE TABLE ban ( + id SERIAL NOT NULL, + email TEXT, + mailing_list TEXT, + PRIMARY KEY (id) + ); + +CREATE TABLE uid ( + -- Keep track of all assigned unique ids to prevent re-use. + id SERIAL NOT NULL, + uid UUID, + PRIMARY KEY (id) + ); +CREATE INDEX ix_uid_uid ON uid (uid); diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/sql/sqlite.sql index f203f764f..f203f764f 100644 --- a/src/mailman/database/mailman.sql +++ b/src/mailman/database/sql/sqlite.sql diff --git a/src/mailman/database/sqlite.py b/src/mailman/database/sqlite.py new file mode 100644 index 000000000..30c4959b7 --- /dev/null +++ b/src/mailman/database/sqlite.py @@ -0,0 +1,46 @@ +# Copyright (C) 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/>. + +"""SQLite database support.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'SQLiteDatabase', + ] + + +from pkg_resources import resource_string + +from mailman.database.base import StormBaseDatabase + + + +class SQLiteDatabase(StormBaseDatabase): + """Database class for SQLite.""" + + def _database_exists(self, store): + """See `BaseDatabase`.""" + table_query = 'select tbl_name from sqlite_master;' + table_names = set(item[0] for item in + store.execute(table_query)) + return 'version' in table_names + + def _get_schema(self): + """See `BaseDatabase`.""" + return resource_string('mailman.database.sql', 'sqlite.sql') |
