"""Keep immutable document revisions for reindex and historical citations. Revision ID: 0008 Revises: 0007 """ from alembic import op import sqlalchemy as sa revision = "0008" down_revision = "0007" branch_labels = None depends_on = None SOURCE_COLUMNS = ["course_version_id", "project_id", "submission_file_id", "source_hash"] REVISION_CONSTRAINT = "uq_knowledge_document_revision" NAMING = {"uq": "uq_%(table_name)s_%(column_0_name)s"} def upgrade(): inspector = sa.inspect(op.get_bind()) columns = {column["name"] for column in inspector.get_columns("knowledge_documents")} constraints = inspector.get_unique_constraints("knowledge_documents") # Migration 0001 uses current metadata for fresh installs; handle both fresh # schema and a deployed 0007 schema without a revision column. with op.batch_alter_table("knowledge_documents", naming_convention=NAMING) as batch: if "revision" not in columns: batch.add_column(sa.Column("revision", sa.Integer(), nullable=False, server_default="1")) for constraint in constraints: if constraint["column_names"] == SOURCE_COLUMNS: batch.drop_constraint(constraint["name"] or "uq_knowledge_documents_course_version_id", type_="unique") if not any(c["column_names"] == SOURCE_COLUMNS + ["revision"] for c in constraints): batch.create_unique_constraint(REVISION_CONSTRAINT, SOURCE_COLUMNS + ["revision"]) def downgrade(): # Restoring the old uniqueness would require deleting citation sources. Stop # before schema changes rather than silently choosing a surviving revision. duplicates = op.get_bind().execute(sa.text( "SELECT 1 FROM knowledge_documents GROUP BY course_version_id, project_id, " "submission_file_id, source_hash HAVING COUNT(*) > 1 LIMIT 1")).first() if duplicates: raise RuntimeError("存在同源文档的历史修订,不能无损降级;请保留 0008 或恢复完整升级前备份") with op.batch_alter_table("knowledge_documents", naming_convention=NAMING) as batch: batch.drop_constraint(REVISION_CONSTRAINT, type_="unique") batch.create_unique_constraint("uq_knowledge_documents_source", SOURCE_COLUMNS) batch.drop_column("revision")