summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAurélien Bompard2015-09-12 13:28:28 +0200
committerBarry Warsaw2015-09-15 01:33:36 +0000
commit16903aae68c1fa5967595aa94dfc0880214f61f4 (patch)
treea4b195e1f97dc4976675dde7d0fca1af68b59055 /src
parent2f6a250ca38a8846c1d417c380d6311de35c3ee7 (diff)
downloadmailman-16903aae68c1fa5967595aa94dfc0880214f61f4.tar.gz
mailman-16903aae68c1fa5967595aa94dfc0880214f61f4.tar.zst
mailman-16903aae68c1fa5967595aa94dfc0880214f61f4.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/database/alembic/script.py.mako1
-rw-r--r--src/mailman/database/alembic/versions/16c2b25c7b_list_subscription_policy.py9
-rw-r--r--src/mailman/database/alembic/versions/33e1f5f6fa8_.py5
-rw-r--r--src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py15
-rw-r--r--src/mailman/database/alembic/versions/51b7f92bd06c_initial.py10
-rw-r--r--src/mailman/database/tests/test_migrations.py59
-rw-r--r--src/mailman/database/utilities.py40
7 files changed, 125 insertions, 14 deletions
diff --git a/src/mailman/database/alembic/script.py.mako b/src/mailman/database/alembic/script.py.mako
index 95702017e..acc126c54 100644
--- a/src/mailman/database/alembic/script.py.mako
+++ b/src/mailman/database/alembic/script.py.mako
@@ -12,6 +12,7 @@ down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
+from mailman.database.utilities import is_sqlite, exists_in_db
${imports if imports else ""}
def upgrade():
diff --git a/src/mailman/database/alembic/versions/16c2b25c7b_list_subscription_policy.py b/src/mailman/database/alembic/versions/16c2b25c7b_list_subscription_policy.py
index 8534f4b73..1d21735c6 100644
--- a/src/mailman/database/alembic/versions/16c2b25c7b_list_subscription_policy.py
+++ b/src/mailman/database/alembic/versions/16c2b25c7b_list_subscription_policy.py
@@ -14,14 +14,17 @@ from alembic import op
import sqlalchemy as sa
from mailman.database.types import Enum
+from mailman.database.utilities import is_sqlite, exists_in_db
from mailman.interfaces.mailinglist import SubscriptionPolicy
def upgrade():
### Update the schema
- op.add_column('mailinglist', sa.Column(
- 'subscription_policy', Enum(SubscriptionPolicy), nullable=True))
+ if not exists_in_db(op.get_bind(), 'mailinglist', 'subscription_policy'):
+ # SQL may not have removed it when downgrading.
+ op.add_column('mailinglist', sa.Column(
+ 'subscription_policy', Enum(SubscriptionPolicy), nullable=True))
### Now migrate the data
# don't import the table definition from the models, it may break this
@@ -36,6 +39,6 @@ def upgrade():
def downgrade():
- if op.get_bind().dialect.name != 'sqlite':
+ if not is_sqlite(op.get_bind()):
# SQLite does not support dropping columns.
op.drop_column('mailinglist', 'subscription_policy')
diff --git a/src/mailman/database/alembic/versions/33e1f5f6fa8_.py b/src/mailman/database/alembic/versions/33e1f5f6fa8_.py
index 14a7a2b74..45d5929d9 100644
--- a/src/mailman/database/alembic/versions/33e1f5f6fa8_.py
+++ b/src/mailman/database/alembic/versions/33e1f5f6fa8_.py
@@ -33,6 +33,7 @@ __all__ = [
from alembic import op
import sqlalchemy as sa
+from mailman.database.utilities import is_sqlite
# revision identifiers, used by Alembic.
@@ -41,7 +42,7 @@ down_revision = '51b7f92bd06c'
def upgrade():
- if op.get_bind().dialect.name != 'sqlite':
+ if not is_sqlite(op.get_bind()):
# SQLite does not support altering columns.
op.alter_column('message', 'message_id_hash', type_=sa.Unicode)
op.alter_column('message', 'path', type_=sa.Unicode)
@@ -51,7 +52,7 @@ def upgrade():
def downgrade():
- if op.get_bind().dialect.name != 'sqlite':
+ if not is_sqlite(op.get_bind()):
# SQLite does not support altering columns.
op.alter_column('message', 'message_id_hash', type_=sa.LargeBinary)
op.alter_column('message', 'path', type_=sa.LargeBinary)
diff --git a/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py b/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py
index fc489cae5..826961d73 100644
--- a/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py
+++ b/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py
@@ -29,6 +29,7 @@ down_revision = '33e1f5f6fa8'
from alembic import op
import sqlalchemy as sa
+from mailman.database.utilities import is_sqlite, exists_in_db
def upgrade():
@@ -40,16 +41,20 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('user_id', 'domain_id')
)
- op.add_column(
- 'user',
- sa.Column('is_server_owner', sa.Boolean(), nullable=True))
- if op.get_bind().dialect.name != 'sqlite':
+ if not exists_in_db(op.get_bind(), 'user', 'is_server_owner'):
+ # SQL may not have removed it when downgrading.
+ op.add_column(
+ 'user',
+ sa.Column('is_server_owner', sa.Boolean(), nullable=True))
+ if not is_sqlite(op.get_bind()):
op.drop_column('domain', 'contact_address')
def downgrade():
- if op.get_bind().dialect.name != 'sqlite':
+ if not is_sqlite(op.get_bind()):
op.drop_column('user', 'is_server_owner')
+ if not exists_in_db(op.get_bind(), 'domain', 'contact_address'):
+ # SQLite may not have removed it.
op.add_column(
'domain',
sa.Column('contact_address', sa.VARCHAR(), nullable=True))
diff --git a/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py b/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
index 763554b67..ff55e5174 100644
--- a/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
+++ b/src/mailman/database/alembic/versions/51b7f92bd06c_initial.py
@@ -37,6 +37,7 @@ __all__ = [
from alembic import op
import sqlalchemy as sa
+from mailman.database.utilities import is_sqlite, exists_in_db
# Revision identifiers, used by Alembic.
@@ -46,7 +47,7 @@ down_revision = None
def upgrade():
op.drop_table('version')
- if op.get_bind().dialect.name != 'sqlite':
+ if not is_sqlite(op.get_bind()):
# SQLite does not support dropping columns.
op.drop_column('mailinglist', 'acceptable_aliases_id')
op.create_index(op.f('ix_user__user_id'), 'user',
@@ -58,6 +59,7 @@ def downgrade():
op.create_table('version')
op.create_index('ix_user_user_id', 'user', ['_user_id'], unique=False)
op.drop_index(op.f('ix_user__user_id'), table_name='user')
- op.add_column(
- 'mailinglist',
- sa.Column('acceptable_aliases_id', sa.INTEGER(), nullable=True))
+ if not exists_in_db(op.get_bind(), 'mailinglist', 'acceptable_aliases_id'):
+ op.add_column(
+ 'mailinglist',
+ sa.Column('acceptable_aliases_id', sa.INTEGER(), nullable=True))
diff --git a/src/mailman/database/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py
new file mode 100644
index 000000000..201fd618a
--- /dev/null
+++ b/src/mailman/database/tests/test_migrations.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2013-2015 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/>.
+
+"""Test database schema migrations with Alembic"""
+
+__all__ = [
+ 'TestMigrations',
+ ]
+
+
+import unittest
+import alembic.command
+
+from mailman.config import config
+from mailman.database.alembic import alembic_cfg
+from mailman.database.model import Model
+from mailman.testing.layers import ConfigLayer
+import sqlalchemy as sa
+
+
+
+class TestMigrations(unittest.TestCase):
+
+ layer = ConfigLayer
+
+ def setUp(self):
+ alembic.command.stamp(alembic_cfg, 'head')
+
+ def tearDown(self):
+ # Drop and restore a virgin database.
+ md = sa.MetaData()
+ md.reflect(bind=config.db.engine)
+ for table in md.sorted_tables:
+ table.drop(config.db.engine)
+ Model.metadata.create_all(config.db.engine)
+
+ def test_all_migrations(self):
+ script_dir = alembic.script.ScriptDirectory.from_config(alembic_cfg)
+ revisions = [sc.revision for sc in
+ script_dir.walk_revisions("base", "heads")]
+ for revision in revisions:
+ alembic.command.downgrade(alembic_cfg, revision)
+ revisions.reverse()
+ for revision in revisions:
+ alembic.command.upgrade(alembic_cfg, revision)
diff --git a/src/mailman/database/utilities.py b/src/mailman/database/utilities.py
new file mode 100644
index 000000000..0db4848ca
--- /dev/null
+++ b/src/mailman/database/utilities.py
@@ -0,0 +1,40 @@
+# Copyright (C) 2006-2015 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/>.
+
+"""Common database utilities."""
+
+__all__ = [
+ 'exists_in_db',
+ ]
+
+import sqlalchemy as sa
+
+
+def is_sqlite(bind):
+ return bind.dialect.name == 'sqlite'
+
+
+def exists_in_db(bind, tablename, columnname=None):
+ md = sa.MetaData()
+ md.reflect(bind=bind)
+ if columnname is None:
+ return tablename in md.tables
+ else:
+ return (
+ tablename in md.tables and
+ columnname in [c.name for c in md.tables[tablename].columns]
+ )