"""Replace saved dataset inputs with editable preparations and independent snapshots.""" import sqlalchemy as sa from alembic import op revision = "0015" down_revision = "0014" branch_labels = None depends_on = None def upgrade(): op.drop_table("template_inputs") op.add_column("catalog_batches", sa.Column("job_id", sa.String(36), nullable=True)) op.add_column("catalog_batches", sa.Column("offset", sa.Integer(), nullable=False, server_default="0")) op.execute("UPDATE catalog_batches SET job_id = id") # Preserve unfinished catalog pagination; only legacy research inputs are discarded. jobs = sa.table("sync_jobs", sa.column("id"), sa.column("checkpoint", sa.JSON())) batches = sa.table("catalog_batches", sa.column("id"), sa.column("offset", sa.Integer())) for job_id, checkpoint in op.get_bind().execute(sa.select(jobs.c.id, jobs.c.checkpoint)): offset = (checkpoint or {}).get("offset", 0) if type(offset) is int and offset >= 0: op.get_bind().execute(batches.update().where(batches.c.id == job_id).values(offset=offset)) names = {"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s"} fk = next( f for f in sa.inspect(op.get_bind()).get_foreign_keys("catalog_batches") if f["constrained_columns"] == ["id"] ) with op.batch_alter_table("catalog_batches", naming_convention=names) as batch: batch.drop_constraint(fk["name"] or "fk_catalog_batches_id_sync_jobs", type_="foreignkey") batch.alter_column("job_id", existing_type=sa.String(36), nullable=False) batch.create_foreign_key("fk_catalog_batches_job", "sync_jobs", ["job_id"], ["id"]) batch.create_index("ix_catalog_batches_job_id", ["job_id"]) op.create_table( "data_preparations", sa.Column("id", sa.String(36), primary_key=True), sa.Column("name", sa.String(200), nullable=False), sa.Column("note", sa.Text(), nullable=False), sa.Column("scope_key", sa.String(200), nullable=False), sa.Column("scope", sa.JSON(), nullable=False), sa.Column("version", sa.Integer(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), ) op.create_index("ix_data_preparations_scope_key", "data_preparations", ["scope_key"]) op.create_table( "preparation_fields", sa.Column( "preparation_id", sa.String(36), sa.ForeignKey("data_preparations.id", ondelete="CASCADE"), primary_key=True, ), sa.Column("field_id", sa.String(200), primary_key=True), sa.Column("dataset_id", sa.String(200), nullable=False), sa.Column("content", sa.JSON(), nullable=False), ) op.create_index("ix_preparation_fields_dataset_id", "preparation_fields", ["dataset_id"]) op.create_table( "research_input_snapshots", sa.Column("id", sa.String(36), primary_key=True), sa.Column("preparation_id", sa.String(36), nullable=False), sa.Column("preparation_version", sa.Integer(), nullable=False), sa.Column("content", sa.JSON(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.UniqueConstraint("preparation_id", "preparation_version"), ) op.create_index( "ix_research_input_snapshots_preparation_id", "research_input_snapshots", ["preparation_id"] ) def downgrade(): raise RuntimeError("旧输入模型已移除;回退请恢复升级前数据库备份")