fix: harden strategy set migration upgrade

This commit is contained in:
2026-04-03 08:48:01 +00:00
parent 1b0991c9cf
commit 3d37186fa7
2 changed files with 31 additions and 4 deletions
+1 -1
View File
@@ -168,5 +168,5 @@ When schema changes:
| Revision ID | File | Summary |
|---|---|---|
| 20260403_0001 | migrations/versions/20260403_0001_initial_portfolio_schema.py | Initial schema for strategy sets, portfolios, ledger, indexes, optional hypertable |
| 20260403_0002 | migrations/versions/20260403_0002_strategy_set_columns.py | Rename `strategy_name`->`name`, `strategy_config`->`config`, add unique `nick` |
| 20260403_0002 | migrations/versions/20260403_0002_strategy_set_columns.py | Rename `strategy_name`->`name`, `strategy_config`->`config`, add unique `nick`, handle legacy unnamed unique constraint |
| 20260403_0003 | migrations/versions/20260403_0003_portfolio_strategy_refs.py | Remove embedded portfolio strategy fields, add `active_strategy_id` and `avabile_strategies` |
@@ -17,6 +17,33 @@ branch_labels = None
depends_on = None
def _drop_unique_constraint_for_columns(table_name: str, columns: tuple[str, ...]) -> None:
bind = op.get_bind()
rows = bind.execute(
sa.text(
"""
SELECT c.conname,
array_agg(a.attname ORDER BY u.ord) AS column_names
FROM pg_constraint c
JOIN pg_class t ON t.oid = c.conrelid
JOIN pg_namespace n ON n.oid = t.relnamespace
JOIN unnest(c.conkey) WITH ORDINALITY AS u(attnum, ord) ON TRUE
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = u.attnum
WHERE c.contype = 'u'
AND n.nspname = current_schema()
AND t.relname = :table_name
GROUP BY c.conname
"""
),
{"table_name": table_name},
)
expected = list(columns)
for row in rows:
if list(row.column_names) == expected:
op.drop_constraint(row.conname, table_name, type_="unique")
return
def upgrade() -> None:
op.add_column("strategy_sets", sa.Column("name", sa.Text(), nullable=True))
op.add_column("strategy_sets", sa.Column("config", sa.Text(), nullable=True, server_default=""))
@@ -38,7 +65,7 @@ def upgrade() -> None:
op.alter_column("strategy_sets", "config", nullable=False)
op.alter_column("strategy_sets", "nick", nullable=False)
op.drop_constraint("uq_strategy_sets_name_config", "strategy_sets", type_="unique")
_drop_unique_constraint_for_columns("strategy_sets", ("strategy_name", "strategy_config"))
op.create_unique_constraint("uq_strategy_sets_name_config", "strategy_sets", ["name", "config"])
op.create_unique_constraint("uq_strategy_sets_nick", "strategy_sets", ["nick"])
@@ -64,8 +91,8 @@ def downgrade() -> None:
op.alter_column("strategy_sets", "strategy_name", nullable=False)
op.alter_column("strategy_sets", "strategy_config", nullable=False)
op.drop_constraint("uq_strategy_sets_nick", "strategy_sets", type_="unique")
op.drop_constraint("uq_strategy_sets_name_config", "strategy_sets", type_="unique")
_drop_unique_constraint_for_columns("strategy_sets", ("nick",))
_drop_unique_constraint_for_columns("strategy_sets", ("name", "config"))
op.create_unique_constraint("uq_strategy_sets_name_config", "strategy_sets", ["strategy_name", "strategy_config"])
op.drop_column("strategy_sets", "nick")