fix: make migration 0002 robust with table existence checks

Migration was failing on DBs where referral_earnings or
advertising_campaign_registrations tables didn't exist yet,
causing campaign_id column to never be added. Added _has_table
and _has_column guards, wrapped backfill in existence check.
This commit is contained in:
Fringg
2026-02-18 11:30:38 +03:00
parent a7f3d652c5
commit f076269c32
@@ -20,42 +20,59 @@ branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Add column (idempotent check)
def _has_table(name: str) -> bool:
conn = op.get_bind()
inspector = sa.inspect(conn)
columns = [c['name'] for c in inspector.get_columns('referral_earnings')]
return name in inspector.get_table_names()
if 'campaign_id' not in columns:
def _has_column(table: str, column: str) -> bool:
conn = op.get_bind()
inspector = sa.inspect(conn)
return column in [c['name'] for c in inspector.get_columns(table)]
def upgrade() -> None:
# Skip if referral_earnings table doesn't exist yet
# (fresh DBs create it via create_all in 0001 with campaign_id already present)
if not _has_table('referral_earnings'):
return
if not _has_column('referral_earnings', 'campaign_id'):
op.add_column('referral_earnings', sa.Column('campaign_id', sa.Integer(), nullable=True))
op.create_foreign_key(
'fk_referral_earnings_campaign_id',
'referral_earnings',
'advertising_campaigns',
['campaign_id'],
['id'],
ondelete='SET NULL',
)
# Only create FK if target table exists
if _has_table('advertising_campaigns'):
op.create_foreign_key(
'fk_referral_earnings_campaign_id',
'referral_earnings',
'advertising_campaigns',
['campaign_id'],
['id'],
ondelete='SET NULL',
)
op.create_index('ix_referral_earnings_campaign_id', 'referral_earnings', ['campaign_id'])
# Backfill existing data — pick earliest campaign registration per user
# (matches runtime logic in get_user_campaign_id: ORDER BY created_at ASC LIMIT 1)
op.execute(
"""
UPDATE referral_earnings re
SET campaign_id = sub.campaign_id
FROM (
SELECT DISTINCT ON (user_id) user_id, campaign_id
FROM advertising_campaign_registrations
ORDER BY user_id, created_at ASC
) sub
WHERE sub.user_id = re.referral_id
AND re.campaign_id IS NULL
"""
)
# Backfill existing data — only if source table exists
if _has_table('advertising_campaign_registrations') and _has_table('referral_earnings'):
op.execute(
sa.text("""
UPDATE referral_earnings re
SET campaign_id = sub.campaign_id
FROM (
SELECT DISTINCT ON (user_id) user_id, campaign_id
FROM advertising_campaign_registrations
ORDER BY user_id, created_at ASC
) sub
WHERE sub.user_id = re.referral_id
AND re.campaign_id IS NULL
""")
)
def downgrade() -> None:
op.drop_index('ix_referral_earnings_campaign_id', table_name='referral_earnings')
op.drop_constraint('fk_referral_earnings_campaign_id', 'referral_earnings', type_='foreignkey')
op.drop_column('referral_earnings', 'campaign_id')
if _has_table('referral_earnings') and _has_column('referral_earnings', 'campaign_id'):
op.drop_index('ix_referral_earnings_campaign_id', table_name='referral_earnings')
op.drop_constraint('fk_referral_earnings_campaign_id', 'referral_earnings', type_='foreignkey')
op.drop_column('referral_earnings', 'campaign_id')