Files
LT_Janitorial_Quality_Control/migrations/versions/phase54_issue_links.py
T

73 lines
2.7 KiB
Python

"""phase54 — link related and duplicate issues
Creates `issue_links`: one row per connection between two issues, displayed on
both of them. See the IssueLink docstring in app/models/issue.py for why the
direction is stored once rather than mirrored.
Purely additive. Nothing reads the table until a person creates a link, so an
empty table is exactly today's behaviour and there is nothing to backfill.
Both FKs use ON DELETE CASCADE so deleting an issue takes its links with it from
either end — a surviving link pointing at a deleted issue would render a dead
row on the other issue's page. The ORM cascade on Issue.links_from/links_to
covers the application path; this covers a direct SQL delete.
Table-existence check — safe to re-run.
Revision ID: phase54_issue_links
Revises: phase53_followup_assignee
"""
from alembic import op
import sqlalchemy as sa
# Revision ids must be <= 32 chars — alembic_version.version_num is VARCHAR(32).
revision = 'phase54_issue_links'
down_revision = 'phase53_followup_assignee'
branch_labels = None
depends_on = None
def _table_exists(conn, name):
return conn.execute(sa.text("""
SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t
"""), {'t': name}).scalar() > 0
def upgrade():
conn = op.get_bind()
if _table_exists(conn, 'issue_links'):
return
op.execute(sa.text("""
CREATE TABLE issue_links (
id INT AUTO_INCREMENT PRIMARY KEY,
issue_id INT NOT NULL,
linked_issue_id INT NOT NULL,
link_type ENUM('duplicate','related') NOT NULL DEFAULT 'related',
created_by INT NULL,
created_at DATETIME NOT NULL,
CONSTRAINT uq_issue_link UNIQUE (issue_id, linked_issue_id),
-- Names match what SQLAlchemy's index=True generates, so the
-- schema this migration builds and the one db.create_all() builds
-- are identical down to the index names.
INDEX ix_issue_links_issue_id (issue_id),
INDEX ix_issue_links_linked_issue_id (linked_issue_id),
CONSTRAINT fk_issue_links_issue
FOREIGN KEY (issue_id) REFERENCES issues (id) ON DELETE CASCADE,
CONSTRAINT fk_issue_links_linked
FOREIGN KEY (linked_issue_id) REFERENCES issues (id) ON DELETE CASCADE,
CONSTRAINT fk_issue_links_creator
FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
"""))
def downgrade():
conn = op.get_bind()
if _table_exists(conn, 'issue_links'):
op.execute(sa.text('DROP TABLE issue_links'))