feat: BAT-025 add portfolio ledger manual metadata migration
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""portfolio_ledger: manual ledger metadata columns backfill-safe upgrade
|
||||
|
||||
Revision ID: 20260715_0028
|
||||
Revises: 20260628_0027
|
||||
Create Date: 2026-07-15 00:00:00
|
||||
|
||||
This migration adds the portfolio_ledger additive metadata columns introduced by
|
||||
REQ-056. It is safe for both fresh DBs and legacy DBs where ensure_schema() already
|
||||
created columns at runtime.
|
||||
|
||||
- source: TEXT NOT NULL DEFAULT 'broker'
|
||||
- target_ledger_entry_id: BIGINT NULL
|
||||
- confirmation_evidence: TEXT NULL
|
||||
- actor_user_id: TEXT NULL
|
||||
|
||||
Runtime behavior:
|
||||
- if source exists, backfill NULL -> 'broker', then enforce NOT NULL/default.
|
||||
- if source/other columns are absent, add them as idempotent ALTERs.
|
||||
- existing rows are preserved; only metadata defaults/backfill are adjusted.
|
||||
- downgrade is forward-only (columns are not removed).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import inspect, text
|
||||
|
||||
|
||||
revision = "20260715_0028"
|
||||
down_revision = "20260628_0027"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _table_exists(inspector: sa.engine.Inspector, table_name: str) -> bool:
|
||||
return table_name in inspector.get_table_names()
|
||||
|
||||
|
||||
def _column_exists(inspector: sa.engine.Inspector, table_name: str, column_name: str) -> bool:
|
||||
if not _table_exists(inspector, table_name):
|
||||
return False
|
||||
return any(c["name"] == column_name for c in inspector.get_columns(table_name))
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
|
||||
if not _table_exists(inspector, "portfolio_ledger"):
|
||||
return
|
||||
|
||||
if not _column_exists(inspector, "portfolio_ledger", "source"):
|
||||
op.add_column(
|
||||
"portfolio_ledger",
|
||||
sa.Column(
|
||||
"source",
|
||||
sa.Text(),
|
||||
nullable=False,
|
||||
server_default=sa.text("'broker'"),
|
||||
),
|
||||
)
|
||||
else:
|
||||
# runtime-precreated 환경: source가 NULL이면 명시적으로 backfill 후 NOT NULL + 기본값 보장
|
||||
bind.execute(text("UPDATE portfolio_ledger SET source = 'broker' WHERE source IS NULL"))
|
||||
op.alter_column(
|
||||
"portfolio_ledger",
|
||||
"source",
|
||||
existing_type=sa.Text(),
|
||||
server_default=sa.text("'broker'"),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
if not _column_exists(inspector, "portfolio_ledger", "target_ledger_entry_id"):
|
||||
op.add_column(
|
||||
"portfolio_ledger",
|
||||
sa.Column("target_ledger_entry_id", sa.BigInteger(), nullable=True),
|
||||
)
|
||||
|
||||
if not _column_exists(inspector, "portfolio_ledger", "confirmation_evidence"):
|
||||
op.add_column(
|
||||
"portfolio_ledger",
|
||||
sa.Column("confirmation_evidence", sa.Text(), nullable=True),
|
||||
)
|
||||
|
||||
if not _column_exists(inspector, "portfolio_ledger", "actor_user_id"):
|
||||
op.add_column(
|
||||
"portfolio_ledger",
|
||||
sa.Column("actor_user_id", sa.Text(), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
# Forward-only policy: 운영 rollback은 컬럼 삭제/재적용 경로가 아니다.
|
||||
def downgrade() -> None:
|
||||
"""Forward-only migration: 컬럼 삭제 downgrade는 수행하지 않는다."""
|
||||
return
|
||||
Reference in New Issue
Block a user