package common import ( "github.com/emvi/logbuch" "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" "gorm.io/gorm" ) var customPreMigrations []migrationFunc func init() { customPreMigrations = []migrationFunc{ { f: func(db *gorm.DB, cfg *config.Config) error { migrator := db.Migrator() oldTableName, newTableName := "custom_rules", "language_mappings" oldIndexName, newIndexName := "idx_customrule_user", "idx_language_mapping_user" if migrator.HasTable(oldTableName) { logbuch.Info("renaming '%s' table to '%s'", oldTableName, newTableName) if err := migrator.RenameTable(oldTableName, &models.LanguageMapping{}); err != nil { return err } logbuch.Info("renaming '%s' index to '%s'", oldIndexName, newIndexName) return migrator.RenameIndex(&models.LanguageMapping{}, oldIndexName, newIndexName) } return nil }, name: "rename language mappings table", }, { f: func(db *gorm.DB, cfg *config.Config) error { // drop all already existing foreign key constraints // afterwards let them be re-created by auto migrate with the newly introduced cascade settings, migrator := db.Migrator() const lookupKey = "20201106-migration_cascade_constraints" if cfg.Db.Dialect == config.SQLDialectSqlite { // https://stackoverflow.com/a/1884893/3112139 // unfortunately, we can't migrate existing sqlite databases to the newly introduced cascade settings // things like deleting all summaries won't work in those cases unless an entirely new db is created logbuch.Info("not attempting to drop and regenerate constraints on sqlite") return nil } if !migrator.HasTable(&models.KeyStringValue{}) { logbuch.Info("key-value table not yet existing") return nil } condition := "key = ?" if cfg.Db.Dialect == config.SQLDialectMysql { condition = "`key` = ?" } lookupResult := db.Where(condition, lookupKey).First(&models.KeyStringValue{}) if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { logbuch.Info("no need to migrate cascade constraints") return nil } // SELECT * FROM INFORMATION_SCHEMA.table_constraints; constraints := map[string]interface{}{ "fk_summaries_editors": &models.SummaryItem{}, "fk_summaries_languages": &models.SummaryItem{}, "fk_summaries_machines": &models.SummaryItem{}, "fk_summaries_operating_systems": &models.SummaryItem{}, "fk_summaries_projects": &models.SummaryItem{}, "fk_summary_items_summary": &models.SummaryItem{}, "fk_summaries_user": &models.Summary{}, "fk_language_mappings_user": &models.LanguageMapping{}, "fk_heartbeats_user": &models.Heartbeat{}, "fk_aliases_user": &models.Alias{}, } for name, table := range constraints { if migrator.HasConstraint(table, name) { logbuch.Info("dropping constraint '%s'", name) if err := migrator.DropConstraint(table, name); err != nil { return err } } } if err := db.Create(&models.KeyStringValue{ Key: lookupKey, Value: "done", }).Error; err != nil { return err } return nil }, name: "add cascade constraints", }, } } func RunCustomPreMigrations(db *gorm.DB, cfg *config.Config) { for _, m := range customPreMigrations { logbuch.Info("potentially running migration '%s'", m.name) if err := m.f(db, cfg); err != nil { logbuch.Fatal("migration '%s' failed – %v", m.name, err) } } }