diff --git a/run_migrations.py b/run_migrations.py index 5a98939..d5f3e84 100644 --- a/run_migrations.py +++ b/run_migrations.py @@ -71,40 +71,57 @@ def ensure_tracking_table(conn): def seed_pre_tracking_migrations(conn): """ Retroactively mark migrations as applied if their schema objects already - exist. Required when the tracking table is introduced to a database that - was migrated before tracking existed — prevents re-running non-idempotent - statements (TimescaleDB policies, triggers, continuous aggregates). + exist. Checked on every startup — safe to run repeatedly (ON CONFLICT DO + NOTHING). Prevents re-running non-idempotent statements when a second + container starts after another has already applied the migration, or when + the tracking table is introduced to a database migrated before it existed. + + Sentinel objects per migration: + 02 — tracksolid.devices table exists + 03 — position_history.altitude column exists + 04 — trips.distance_km column exists (renamed from distance_m) + 05 — tracksolid.device_events table exists (new in 05) """ + checks = [ + ( + "02_tracksolid_full_schema_rev.sql", + "SELECT 1 FROM information_schema.tables " + "WHERE table_schema='tracksolid' AND table_name='devices'", + ), + ( + "03_webhook_schema_migration.sql", + "SELECT 1 FROM information_schema.columns " + "WHERE table_schema='tracksolid' AND table_name='position_history' " + "AND column_name='altitude'", + ), + ( + "04_bug_fix_migration.sql", + "SELECT 1 FROM information_schema.columns " + "WHERE table_schema='tracksolid' AND table_name='trips' " + "AND column_name='distance_km'", + ), + ( + "05_enhancement_migration.sql", + "SELECT 1 FROM information_schema.tables " + "WHERE table_schema='tracksolid' AND table_name='device_events'", + ), + ] + seeds = [] - with conn.cursor() as cur: - # Migration 02: tracksolid.devices is the canonical sentinel table - cur.execute(""" - SELECT 1 FROM information_schema.tables - WHERE table_schema = 'tracksolid' AND table_name = 'devices' - """) - if cur.fetchone(): - seeds.append("02_tracksolid_full_schema_rev.sql") - - # Migration 03: position_history.altitude column added in this migration - cur.execute(""" - SELECT 1 FROM information_schema.columns - WHERE table_schema = 'tracksolid' - AND table_name = 'position_history' - AND column_name = 'altitude' - """) - if cur.fetchone(): - seeds.append("03_webhook_schema_migration.sql") - - for filename in seeds: - cur.execute(""" - INSERT INTO tracksolid.schema_migrations (filename) - VALUES (%s) ON CONFLICT DO NOTHING - """, (filename,)) + for filename, query in checks: + cur.execute(query) + if cur.fetchone(): + cur.execute( + "INSERT INTO tracksolid.schema_migrations (filename) " + "VALUES (%s) ON CONFLICT DO NOTHING", + (filename,), + ) + seeds.append(filename) conn.commit() if seeds: - print(f" Seeded pre-tracking migrations as applied: {', '.join(seeds)}") + print(f" Seeded as applied: {', '.join(seeds)}") def already_applied(conn, filename):