From 30597a73db32ee67a20ce05856df8792c71b9081 Mon Sep 17 00:00:00 2001 From: Thomas Rademaker Date: Fri, 20 Mar 2026 13:52:18 -0400 Subject: [PATCH] more database migration fixes --- appview/database/migrations.go | 80 ++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/appview/database/migrations.go b/appview/database/migrations.go index a0a9065..45b94e2 100644 --- a/appview/database/migrations.go +++ b/appview/database/migrations.go @@ -7,40 +7,72 @@ import ( "gorm.io/gorm" ) -// fixColumnNames renames misnamed columns created by GORM's default snake_case -// conversion of acronym field names (e.g. DID → d_id instead of did). -// Safe to run repeatedly — skips tables/columns that don't need fixing. -func fixColumnNames(db *gorm.DB) { - renames := []struct { - table, from, to string +// dropStaleColumns removes leftover columns created by GORM's broken snake_case +// conversion of acronym field names (DID → d_id, ATURI → a_t_u_r_i, etc.). +// The correct columns (did, at_uri, subject_did) already exist; these are duplicates. +func dropStaleColumns(db *gorm.DB) { + drops := []struct { + table, column string }{ - {"subscriptions", "d_id", "did"}, - {"comments", "d_id", "did"}, - {"comments", "a_t_u_r_i", "at_uri"}, - {"recommendations", "d_id", "did"}, - {"podcast_lists", "d_id", "did"}, - {"bookmarks", "d_id", "did"}, - {"profiles", "d_id", "did"}, - {"episode_states", "d_id", "did"}, - {"blocks", "d_id", "did"}, - {"blocks", "a_t_u_r_i", "at_uri"}, - {"blocks", "subject_d_i_d", "subject_did"}, - {"reports", "d_id", "did"}, + {"subscriptions", "d_id"}, + {"comments", "d_id"}, + {"comments", "a_t_u_r_i"}, + {"recommendations", "d_id"}, + {"podcast_lists", "d_id"}, + {"bookmarks", "d_id"}, + {"profiles", "d_id"}, + {"episode_states", "d_id"}, + {"blocks", "d_id"}, + {"blocks", "a_t_u_r_i"}, + {"blocks", "subject_d_i_d"}, + {"reports", "d_id"}, } - for _, r := range renames { - if db.Migrator().HasColumn(r.table, r.from) { - if err := db.Exec(fmt.Sprintf(`ALTER TABLE %q RENAME COLUMN %q TO %q`, r.table, r.from, r.to)).Error; err != nil { - slog.Warn("column rename failed (may already be correct)", "table", r.table, "from", r.from, "to", r.to, "err", err) + for _, d := range drops { + if db.Migrator().HasColumn(d.table, d.column) { + if err := db.Exec(fmt.Sprintf(`ALTER TABLE %q DROP COLUMN %q`, d.table, d.column)).Error; err != nil { + slog.Warn("drop column failed", "table", d.table, "column", d.column, "err", err) } else { - slog.Info("renamed column", "table", r.table, "from", r.from, "to", r.to) + slog.Info("dropped stale column", "table", d.table, "column", d.column) } } } } +// widenIntColumns changes integer columns to bigint where PodcastIndex IDs +// can exceed int4 max (~2.1 billion). Safe to run repeatedly. +func widenIntColumns(db *gorm.DB) { + alters := []struct { + table, column string + }{ + {"subscriptions", "feed_id"}, + {"comments", "feed_id"}, + {"comments", "episode_id"}, + {"recommendations", "feed_id"}, + {"recommendations", "episode_id"}, + {"bookmarks", "feed_id"}, + {"bookmarks", "episode_id"}, + {"podcast_stats", "feed_id"}, + {"episode_stats", "episode_id"}, + {"episode_stats", "feed_id"}, + {"episode_states", "feed_id"}, + {"episode_states", "episode_id"}, + } + + for _, a := range alters { + if !db.Migrator().HasTable(a.table) { + continue + } + sql := fmt.Sprintf(`ALTER TABLE %q ALTER COLUMN %q TYPE bigint`, a.table, a.column) + if err := db.Exec(sql).Error; err != nil { + slog.Warn("widen column failed (may already be bigint)", "table", a.table, "column", a.column, "err", err) + } + } +} + func AutoMigrateAll(db *gorm.DB) error { - fixColumnNames(db) + dropStaleColumns(db) + widenIntColumns(db) if err := db.AutoMigrate( &FirehoseCursor{}, -- 2.51.2