skip some alembic migrations for sqlite

SQLite does not support many of the DDL instructions to modify
tables. We only use it for development and unit tests, so we don't
need to support it fully in the alembic migrations.

This patch updates the tests to use sqlalchemy's create_all() function
to initialize the sqlite database, and then skips the migrations or
portions of migrations that would not work for sqlite.

This has 2 benefits: creating the database in the unit tests is faster
and it should simplify any work we need to do to remove constraints
from tables or otherwise change the database schema, since those
changes will only need to be run for mysql tests and not the sqlite
tests.

Change-Id: I6658596ca3687595596d09428f97f9a82f9062ac
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
Doug Hellmann 2018-09-23 15:47:57 -04:00 committed by Andreas Jaeger
parent a5d8b2b64d
commit e23eba1ba8
17 changed files with 470 additions and 361 deletions

View File

@ -61,337 +61,345 @@ def _define_enums():
def upgrade(active_plugins=None, options=None):
dialect = op.get_bind().engine.dialect
enums = _define_enums()
op.create_table(
'project_groups',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_group_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'user_preferences',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('key', sa.Unicode(100), nullable=False),
sa.Column('type', enums['pref_type'], nullable=False),
sa.Column('value', sa.Unicode(255), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('full_name', sa.Unicode(255), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('openid', sa.String(255)),
sa.Column('is_staff', sa.Boolean(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=True),
sa.Column('is_superuser', sa.Boolean(), nullable=True),
sa.Column('last_login', sa.DateTime(), nullable=True),
sa.Column('enable_login', sa.Boolean(), default=True,
server_default="1", nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email', name='uniq_user_email'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'teams',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_team_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'permissions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.Unicode(length=50), nullable=True),
sa.Column('codename', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_permission_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'team_membership',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'user_permissions',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('permission_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'team_permissions',
sa.Column('permission_id', sa.Integer(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'stories',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('creator_id', sa.Integer(), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('is_bug', sa.Boolean(), nullable=True),
sa.Column('story_type_id', sa.Integer(), default=1),
sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'projects',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.Column('is_active', sa.Boolean(), default=True,
server_default='1', nullable=False),
sa.Column('repo_url', sa.String(255), default=None, nullable=True),
sa.Column('autocreate_branches', sa.Boolean(), default=False),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_project_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'project_group_mapping',
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('project_group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['project_group_id'], ['project_groups.id'], ),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'tasks',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.Column('status', enums['task_status'], nullable=True),
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('assignee_id', sa.Integer(), nullable=True),
sa.Column('creator_id', sa.Integer(), nullable=True),
sa.Column('priority', enums['task_priority'], nullable=True),
sa.Column('branch_id', sa.Integer(), nullable=True),
sa.Column('milestone_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['assignee_id'], ['users.id'],
name='tasks_ibfk_1'),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'],
name='tasks_ibfk_3'),
sa.ForeignKeyConstraint(['story_id'], ['stories.id'],
name='tasks_ibfk_4'),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'events',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('comment_id', sa.Integer(), nullable=True),
sa.Column('author_id', sa.Integer(), nullable=True),
sa.Column('event_type', sa.Unicode(length=100), nullable=False),
sa.Column('event_info', sa.UnicodeText(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'comments',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('content', type_=MYSQL_MEDIUM_TEXT, nullable=True),
sa.Column('is_active', sa.Boolean(), default=True,
server_default='1', nullable=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'storytags',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_story_tags_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table('story_storytags',
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('storytag_id', sa.Integer(), nullable=True),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'subscriptions',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('target_type', enums['target_type'], nullable=True),
sa.Column('target_id', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'subscription_events',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('subscriber_id', sa.Integer(), nullable=False),
sa.Column('event_type', sa.Unicode(100), nullable=False),
sa.Column('event_info', sa.UnicodeText(), nullable=True),
sa.Column('author_id', sa.Integer()),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'authorizationcodes',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('code', sa.Unicode(100), nullable=False),
sa.Column('state', sa.Unicode(100), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'accesstokens',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('access_token', sa.Unicode(length=100), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_default_charset=MYSQL_CHARSET,
mysql_engine=MYSQL_ENGINE
)
op.create_table(
'refreshtokens',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('refresh_token', sa.Unicode(length=100), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.Column('access_token_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_default_charset=MYSQL_CHARSET,
mysql_engine=MYSQL_ENGINE
)
op.create_table(
'branches',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(100), nullable=True),
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('expired', sa.Boolean(), default=False, nullable=True),
sa.Column('expiration_date', sa.DateTime(), default=None,
nullable=True),
sa.Column('autocreated', sa.Boolean(), default=False, nullable=True),
sa.Column('restricted', sa.Boolean(), default=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', 'project_id', name="branch_un_constr"),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'milestones',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(100), nullable=True),
sa.Column('branch_id', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('expired', sa.Boolean(), default=False, nullable=True),
sa.Column('expiration_date', sa.DateTime(), default=None),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', 'branch_id', name="milestone_un_constr"),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'story_types',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(50), nullable=True),
sa.Column('icon', sa.String(50), nullable=True),
sa.Column('restricted', sa.Boolean(), default=False),
sa.Column('private', sa.Boolean(), default=False),
sa.Column('visible', sa.Boolean(), default=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'may_mutate_to',
sa.Column('story_type_id_from', sa.Integer(), nullable=False),
sa.Column('story_type_id_to', sa.Integer(), nullable=False),
sa.UniqueConstraint('story_type_id_from',
'story_type_id_to',
name="mutate_un_constr"),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
if dialect.supports_alter:
op.create_table(
'project_groups',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_group_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'user_preferences',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('key', sa.Unicode(100), nullable=False),
sa.Column('type', enums['pref_type'], nullable=False),
sa.Column('value', sa.Unicode(255), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('full_name', sa.Unicode(255), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('openid', sa.String(255)),
sa.Column('is_staff', sa.Boolean(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=True),
sa.Column('is_superuser', sa.Boolean(), nullable=True),
sa.Column('last_login', sa.DateTime(), nullable=True),
sa.Column('enable_login', sa.Boolean(), default=True,
server_default="1", nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email', name='uniq_user_email'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'teams',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_team_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'permissions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.Unicode(length=50), nullable=True),
sa.Column('codename', sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_permission_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'team_membership',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'user_permissions',
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('permission_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'team_permissions',
sa.Column('permission_id', sa.Integer(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.ForeignKeyConstraint(['permission_id'], ['permissions.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'stories',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('creator_id', sa.Integer(), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('is_bug', sa.Boolean(), nullable=True),
sa.Column('story_type_id', sa.Integer(), default=1),
sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'projects',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('description', sa.UnicodeText(), nullable=True),
sa.Column('team_id', sa.Integer(), nullable=True),
sa.Column('is_active', sa.Boolean(), default=True,
server_default='1', nullable=False),
sa.Column('repo_url', sa.String(255), default=None, nullable=True),
sa.Column('autocreate_branches', sa.Boolean(), default=False),
sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_project_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'project_group_mapping',
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('project_group_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
['project_group_id'],
['project_groups.id'],
),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'tasks',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('title', sa.Unicode(length=255), nullable=True),
sa.Column('status', enums['task_status'], nullable=True),
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('assignee_id', sa.Integer(), nullable=True),
sa.Column('creator_id', sa.Integer(), nullable=True),
sa.Column('priority', enums['task_priority'], nullable=True),
sa.Column('branch_id', sa.Integer(), nullable=True),
sa.Column('milestone_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['assignee_id'], ['users.id'],
name='tasks_ibfk_1'),
sa.ForeignKeyConstraint(['project_id'], ['projects.id'],
name='tasks_ibfk_3'),
sa.ForeignKeyConstraint(['story_id'], ['stories.id'],
name='tasks_ibfk_4'),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'events',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('comment_id', sa.Integer(), nullable=True),
sa.Column('author_id', sa.Integer(), nullable=True),
sa.Column('event_type', sa.Unicode(length=100), nullable=False),
sa.Column('event_info', sa.UnicodeText(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'comments',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('content', type_=MYSQL_MEDIUM_TEXT, nullable=True),
sa.Column('is_active', sa.Boolean(), default=True,
server_default='1', nullable=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'storytags',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', name='uniq_story_tags_name'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'story_storytags',
sa.Column('story_id', sa.Integer(), nullable=True),
sa.Column('storytag_id', sa.Integer(), nullable=True),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'subscriptions',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('target_type', enums['target_type'], nullable=True),
sa.Column('target_id', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'subscription_events',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('subscriber_id', sa.Integer(), nullable=False),
sa.Column('event_type', sa.Unicode(100), nullable=False),
sa.Column('event_info', sa.UnicodeText(), nullable=True),
sa.Column('author_id', sa.Integer()),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'authorizationcodes',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('code', sa.Unicode(100), nullable=False),
sa.Column('state', sa.Unicode(100), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'accesstokens',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('access_token', sa.Unicode(length=100), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_default_charset=MYSQL_CHARSET,
mysql_engine=MYSQL_ENGINE
)
op.create_table(
'refreshtokens',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('refresh_token', sa.Unicode(length=100), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=False),
sa.Column('expires_in', sa.Integer(), nullable=False),
sa.Column('access_token_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_default_charset=MYSQL_CHARSET,
mysql_engine=MYSQL_ENGINE
)
op.create_table(
'branches',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(100), nullable=True),
sa.Column('project_id', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('expired', sa.Boolean(), default=False, nullable=True),
sa.Column('expiration_date', sa.DateTime(), default=None,
nullable=True),
sa.Column('autocreated', sa.Boolean(), default=False,
nullable=True),
sa.Column('restricted', sa.Boolean(), default=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', 'project_id', name="branch_un_constr"),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'milestones',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(100), nullable=True),
sa.Column('branch_id', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('expired', sa.Boolean(), default=False, nullable=True),
sa.Column('expiration_date', sa.DateTime(), default=None),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name', 'branch_id',
name="milestone_un_constr"),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'story_types',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('name', sa.String(50), nullable=True),
sa.Column('icon', sa.String(50), nullable=True),
sa.Column('restricted', sa.Boolean(), default=False),
sa.Column('private', sa.Boolean(), default=False),
sa.Column('visible', sa.Boolean(), default=True),
sa.PrimaryKeyConstraint('id'),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
op.create_table(
'may_mutate_to',
sa.Column('story_type_id_from', sa.Integer(), nullable=False),
sa.Column('story_type_id_to', sa.Integer(), nullable=False),
sa.UniqueConstraint('story_type_id_from',
'story_type_id_to',
name="mutate_un_constr"),
sa.PrimaryKeyConstraint(),
mysql_engine=MYSQL_ENGINE,
mysql_charset=MYSQL_CHARSET
)
# Create story types
bind = op.get_bind()
@ -454,34 +462,37 @@ def upgrade(active_plugins=None, options=None):
story_type_id_to=1
))
op.create_index('accesstokens_access_token_idx',
'accesstokens', ['access_token'])
if dialect.supports_alter:
op.create_index('accesstokens_access_token_idx',
'accesstokens', ['access_token'])
version_info = op.get_bind().engine.dialect.server_version_info
if version_info[-1] == "MariaDB":
# Removes fake mysql prefix
version_info = version_info[-4:]
if version_info[0] < 5 or version_info[0] == 5 and version_info[1] < 6:
LOG.warning(
"MySQL version is lower than 5.6. Skipping full-text indexes")
return
version_info = op.get_bind().engine.dialect.server_version_info
if version_info[-1] == "MariaDB":
# Removes fake mysql prefix
version_info = version_info[-4:]
if version_info[0] < 5 or version_info[0] == 5 and version_info[1] < 6:
LOG.warning(
"MySQL version is lower than 5.6. Skipping full-text indexes")
return
# Index for projects
op.execute("ALTER TABLE projects "
"ADD FULLTEXT projects_fti (name, description)")
# Index for projects
op.execute("ALTER TABLE projects "
"ADD FULLTEXT projects_fti (name, description)")
# Index for stories
op.execute("ALTER TABLE stories "
"ADD FULLTEXT stories_fti (title, description)")
# Index for stories
op.execute("ALTER TABLE stories "
"ADD FULLTEXT stories_fti (title, description)")
# Index for tasks
op.execute("ALTER TABLE tasks ADD FULLTEXT tasks_fti (title)")
# Index for tasks
op.execute("ALTER TABLE tasks ADD FULLTEXT tasks_fti (title)")
# Index for comments
op.execute("ALTER TABLE comments ADD FULLTEXT comments_fti (content)")
# Index for comments
op.execute("ALTER TABLE comments ADD FULLTEXT comments_fti (content)")
# Index for users
op.execute("ALTER TABLE users ADD FULLTEXT users_fti (full_name, email)")
# Index for users
op.execute(
"ALTER TABLE users ADD FULLTEXT users_fti (full_name, email)"
)
def downgrade(active_plugins=None, options=None):

View File

@ -28,9 +28,12 @@ from alembic import op
import sqlalchemy as sa
import storyboard
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table('worklists',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at',

View File

@ -31,9 +31,11 @@ from sqlalchemy.dialects import mysql
from storyboard.db.api import base as api_base
from storyboard.db.api import boards
from storyboard.db.api import worklists
from storyboard.db.migration import utils
from storyboard.db import models
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table('board_permissions',
sa.Column('board_id', sa.Integer(), nullable=True),

View File

@ -27,7 +27,10 @@ down_revision = '051'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.add_column('tasks', sa.Column('link', sa.UnicodeText(), nullable=True))

View File

@ -28,8 +28,10 @@ from alembic import op
import sqlalchemy as sa
from storyboard.db.decorators import UTCDateTime
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table(
'due_dates',

View File

@ -27,7 +27,10 @@ down_revision = '053'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.add_column(
'worklist_items', sa.Column('archived', sa.Boolean(), nullable=True))

View File

@ -27,7 +27,10 @@ down_revision = '054'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.add_column(
'comments', sa.Column('in_reply_to', sa.Integer(), nullable=True))

View File

@ -29,8 +29,10 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
import storyboard
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table(
'worklist_filters',

View File

@ -37,7 +37,10 @@ from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table(
'story_permissions',

View File

@ -27,11 +27,14 @@ down_revision = '057'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
old_type_enum = sa.Enum('task', 'story', 'project', 'project_group')
new_type_enum = sa.Enum(
'task', 'story', 'project', 'project_group', 'worklist')
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
dialect = op.get_bind().engine.dialect
if dialect.supports_alter:

View File

@ -28,9 +28,11 @@ from alembic import op
import sqlalchemy as sa
from storyboard.db.decorators import UTCDateTime
from storyboard.db.migration import utils
from storyboard.db.models import MYSQL_MEDIUM_TEXT
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_table(
'comments_history',

View File

@ -27,7 +27,10 @@ down_revision = '059'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.add_column(
'events', sa.Column('board_id', sa.Integer(), nullable=True))

View File

@ -27,7 +27,10 @@ down_revision = '060'
from alembic import op
import sqlalchemy as sa
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
dialect = op.get_bind().engine.dialect
if dialect.supports_alter:

View File

@ -26,7 +26,10 @@ down_revision = '062'
from alembic import op
from storyboard.db.migration import utils
@utils.not_sqlite
def upgrade(active_plugins=None, options=None):
op.create_index('story_storytags_idx',
'story_storytags', ['story_id'])

View File

@ -0,0 +1,27 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import functools
from alembic import op
def not_sqlite(f):
"Decorator to skip migrations for sqlite databases."
@functools.wraps(f)
def upgrade(active_plugins=None, options=None):
dialect = op.get_bind().engine.dialect
if dialect.name == 'sqlite':
return
return f(active_plugins, options)
return upgrade

View File

@ -63,6 +63,30 @@ def table_args():
MYSQL_MEDIUM_TEXT = UnicodeText().with_variant(MEDIUMTEXT(), 'mysql')
class ManagedFullText(FullText):
@classmethod
def build_fulltext(cls, table):
"""Override FullText to avoid adding DDL instructions.
This mix-in replaces sqlalchemy_fulltext.FullText and does not
automatically cause the database engine to create the full text
search index columns. We do that using our alembic migration
scripts, instead.
We need this class to support running the unit tests under sqlite,
which doesn't support the full text indexing features of MySQL,
since sqlalchemy-fulltext-search always runs the DDL, even if the
database dialect is not 'mysql'.
After the change in
https://github.com/mengzhuo/sqlalchemy-fulltext-search/pull/15
is approved and released we can remove this class.
"""
return
class CommonLength(object):
top_large_length = 255
top_middle_length = 100
@ -137,7 +161,7 @@ team_membership = Table(
)
class User(FullText, ModelBuilder, Base):
class User(ManagedFullText, ModelBuilder, Base):
__table_args__ = (
schema.UniqueConstraint('email', name='uniq_user_email'),
)
@ -255,7 +279,7 @@ class Permission(ModelBuilder, Base):
# TODO(mordred): Do we really need name and title?
class Project(FullText, ModelBuilder, Base):
class Project(ManagedFullText, ModelBuilder, Base):
"""Represents a software project."""
__table_args__ = (
@ -300,7 +324,7 @@ story_storytags = Table(
)
class Story(FullText, ModelBuilder, Base):
class Story(ManagedFullText, ModelBuilder, Base):
__tablename__ = 'stories'
__fulltext_columns__ = ['title', 'description']
@ -324,7 +348,7 @@ class Story(FullText, ModelBuilder, Base):
"tasks", "events", "tags"]
class Task(FullText, ModelBuilder, Base):
class Task(ManagedFullText, ModelBuilder, Base):
__fulltext_columns__ = ['title']
TASK_STATUSES = {'todo': 'Todo',
@ -504,7 +528,7 @@ class TimeLineEvent(ModelBuilder, Base):
event_info = Column(UnicodeText(), nullable=True)
class Comment(FullText, ModelBuilder, Base):
class Comment(ManagedFullText, ModelBuilder, Base):
__fulltext_columns__ = ['content']
id = Column(Integer, primary_key=True)
@ -514,7 +538,7 @@ class Comment(FullText, ModelBuilder, Base):
parent = relationship('Comment', remote_side=[id], backref='children')
class HistoricalComment(FullText, ModelBuilder, Base):
class HistoricalComment(ManagedFullText, ModelBuilder, Base):
__tablename__ = 'comments_history'
__fulltext_columns__ = ['content']
@ -569,7 +593,7 @@ class WorklistItem(ModelBuilder, Base):
"item_id"]
class FilterCriterion(FullText, ModelBuilder, Base):
class FilterCriterion(ManagedFullText, ModelBuilder, Base):
__tablename__ = "filter_criteria"
__fulltext_columns__ = ['title']
@ -594,7 +618,7 @@ class WorklistFilter(ModelBuilder, Base):
_public_fields = ["id", "list_id", "type"]
class Worklist(FullText, ModelBuilder, Base):
class Worklist(ManagedFullText, ModelBuilder, Base):
__tablename__ = "worklists"
__fulltext_columns__ = ['title']
@ -622,7 +646,7 @@ class BoardWorklist(ModelBuilder, Base):
_public_fields = ["id", "board_id", "list_id", "position"]
class Board(FullText, ModelBuilder, Base):
class Board(ManagedFullText, ModelBuilder, Base):
__tablename__ = "boards"
__fulltext_columns__ = ['title', 'description']
@ -651,7 +675,7 @@ worklist_permissions = Table(
)
class DueDate(FullText, ModelBuilder, Base):
class DueDate(ManagedFullText, ModelBuilder, Base):
__tablename__ = "due_dates"
__fulltext_columns__ = ['name']

View File

@ -18,6 +18,7 @@
import os
import os.path
import shutil
import sqlite3
import stat
import uuid
@ -35,6 +36,7 @@ import testtools
import storyboard.common.working_dir as working_dir
from storyboard.db.api import base as db_api_base
from storyboard.db.migration.cli import get_alembic_config
from storyboard.db import models
import storyboard.tests.mock_data as mock_data
@ -152,9 +154,19 @@ class DbTestCase(WorkingDirTestCase):
CONF.set_override("connection", dburi, group="database")
self._full_db_name = self.test_connection + '/' + self.db_name
LOG.info('using database %s', CONF.database.connection)
LOG.info('test_connection %r', self.test_connection)
if self.test_connection.startswith('sqlite://'):
self.using_sqlite = True
filename = self._full_db_name[9:]
if filename[:2] == '//':
filename = filename[1:]
if not os.path.exists(filename):
# NOTE(dhellmann): Connect and disconnect to create
# the empty database file.
sqlite3.connect(filename).close()
engine = sqlalchemy.create_engine(self._full_db_name)
models.Base.metadata.create_all(engine)
else:
self.using_sqlite = False
# The engine w/o db name