From 259f711f2dbd63b7e1b7d9bea41f9817df4c0c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferdinand=20M=C3=BCtsch?= Date: Wed, 15 Dec 2021 10:50:16 +0100 Subject: [PATCH] fix: migrate id column type to bigint (resolve #281) --- config/config.go | 14 ++++++ .../20201106_migration_cascade_constraints.go | 16 +----- ...2_fix_cascade_for_alias_user_constraint.go | 16 +----- migrations/20210213_add_has_data_field.go | 18 +------ .../20210221_add_created_date_column.go | 18 +------ migrations/20210411_add_imprint_content.go | 18 ++----- ...0210806_remove_persisted_project_labels.go | 17 +------ migrations/20211215_migrate_id_to_bigint.go | 50 +++++++++++++++++++ migrations/shared.go | 30 +++++++++++ models/heartbeat.go | 2 +- models/summary.go | 2 +- 11 files changed, 111 insertions(+), 90 deletions(-) create mode 100644 migrations/20211215_migrate_id_to_bigint.go create mode 100644 migrations/shared.go diff --git a/config/config.go b/config/config.go index 9b15793..e0abe41 100644 --- a/config/config.go +++ b/config/config.go @@ -243,6 +243,14 @@ func (c *dbConfig) IsSQLite() bool { return c.Dialect == "sqlite3" } +func (c *dbConfig) IsMySQL() bool { + return c.Dialect == "mysql" +} + +func (c *dbConfig) IsPostgres() bool { + return c.Dialect == "postgres" +} + func (c *serverConfig) GetPublicUrl() string { return strings.TrimSuffix(c.PublicUrl, "/") } @@ -289,6 +297,12 @@ func resolveDbDialect(dbType string) string { if dbType == "cockroach" { return "postgres" } + if dbType == "sqlite" { + return "sqlite3" + } + if dbType == "mariadb" { + return "mysql" + } return dbType } diff --git a/migrations/20201106_migration_cascade_constraints.go b/migrations/20201106_migration_cascade_constraints.go index cf6f773..e3ece4b 100644 --- a/migrations/20201106_migration_cascade_constraints.go +++ b/migrations/20201106_migration_cascade_constraints.go @@ -31,13 +31,7 @@ func init() { return nil } - condition := "key = ?" - if cfg.Db.Dialect == config.SQLDialectMysql { - condition = "`key` = ?" - } - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) + if hasRun(name, db) { return nil } @@ -64,13 +58,7 @@ func init() { } } - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20210202_fix_cascade_for_alias_user_constraint.go b/migrations/20210202_fix_cascade_for_alias_user_constraint.go index eab15a8..321c751 100644 --- a/migrations/20210202_fix_cascade_for_alias_user_constraint.go +++ b/migrations/20210202_fix_cascade_for_alias_user_constraint.go @@ -26,13 +26,7 @@ func init() { return nil } - condition := "key = ?" - if cfg.Db.Dialect == config.SQLDialectMysql { - condition = "`key` = ?" - } - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) + if hasRun(name, db) { return nil } @@ -43,13 +37,7 @@ func init() { } } - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20210213_add_has_data_field.go b/migrations/20210213_add_has_data_field.go index c4c9a4f..97e34a7 100644 --- a/migrations/20210213_add_has_data_field.go +++ b/migrations/20210213_add_has_data_field.go @@ -1,9 +1,7 @@ package migrations import ( - "github.com/emvi/logbuch" "github.com/muety/wakapi/config" - "github.com/muety/wakapi/models" "gorm.io/gorm" ) @@ -12,13 +10,7 @@ func init() { f := migrationFunc{ name: name, f: func(db *gorm.DB, cfg *config.Config) error { - condition := "key = ?" - if cfg.Db.Dialect == config.SQLDialectMysql { - condition = "`key` = ?" - } - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) + if hasRun(name, db) { return nil } @@ -26,13 +18,7 @@ func init() { return err } - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20210221_add_created_date_column.go b/migrations/20210221_add_created_date_column.go index b77954b..8ad4684 100644 --- a/migrations/20210221_add_created_date_column.go +++ b/migrations/20210221_add_created_date_column.go @@ -1,9 +1,7 @@ package migrations import ( - "github.com/emvi/logbuch" "github.com/muety/wakapi/config" - "github.com/muety/wakapi/models" "gorm.io/gorm" ) @@ -12,13 +10,7 @@ func init() { f := migrationFunc{ name: name, f: func(db *gorm.DB, cfg *config.Config) error { - condition := "key = ?" - if cfg.Db.Dialect == config.SQLDialectMysql { - condition = "`key` = ?" - } - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) + if hasRun(name, db) { return nil } @@ -26,13 +18,7 @@ func init() { return err } - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20210411_add_imprint_content.go b/migrations/20210411_add_imprint_content.go index aa006e3..4adca98 100644 --- a/migrations/20210411_add_imprint_content.go +++ b/migrations/20210411_add_imprint_content.go @@ -1,7 +1,6 @@ package migrations import ( - "github.com/emvi/logbuch" "github.com/muety/wakapi/config" "github.com/muety/wakapi/models" "gorm.io/gorm" @@ -13,15 +12,14 @@ func init() { f := migrationFunc{ name: name, f: func(db *gorm.DB, cfg *config.Config) error { + if hasRun(name, db) { + return nil + } + condition := "key = ?" if cfg.Db.Dialect == config.SQLDialectMysql { condition = "`key` = ?" } - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) - return nil - } imprintKv := &models.KeyStringValue{Key: "imprint", Value: "no content here"} if err := db. @@ -32,13 +30,7 @@ func init() { return err } - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20210806_remove_persisted_project_labels.go b/migrations/20210806_remove_persisted_project_labels.go index d7077ef..8ebfbfc 100644 --- a/migrations/20210806_remove_persisted_project_labels.go +++ b/migrations/20210806_remove_persisted_project_labels.go @@ -13,14 +13,7 @@ func init() { f := migrationFunc{ name: name, f: func(db *gorm.DB, cfg *config.Config) error { - condition := "key = ?" - if cfg.Db.Dialect == config.SQLDialectMysql { - condition = "`key` = ?" - } - - lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) - if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { - logbuch.Info("no need to migrate '%s'", name) + if hasRun(name, db) { return nil } @@ -35,13 +28,7 @@ func init() { } logbuch.Info("successfully deleted project label summary items") - if err := db.Create(&models.KeyStringValue{ - Key: name, - Value: "done", - }).Error; err != nil { - return err - } - + setHasRun(name, db) return nil }, } diff --git a/migrations/20211215_migrate_id_to_bigint.go b/migrations/20211215_migrate_id_to_bigint.go new file mode 100644 index 0000000..9f90a99 --- /dev/null +++ b/migrations/20211215_migrate_id_to_bigint.go @@ -0,0 +1,50 @@ +package migrations + +import ( + "github.com/emvi/logbuch" + "github.com/muety/wakapi/config" + "gorm.io/gorm" +) + +func init() { + const name = "20211215-migrate_id_to_bigint-add_has_data_field" + f := migrationFunc{ + name: name, + f: func(db *gorm.DB, cfg *config.Config) error { + if hasRun(name, db) { + return nil + } + + if cfg.Db.IsMySQL() { + tx := db.Begin() + if err := tx.Exec("ALTER TABLE heartbeats MODIFY COLUMN id BIGINT UNSIGNED").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE summary_items MODIFY COLUMN id BIGINT UNSIGNED").Error; err != nil { + return err + } + tx.Commit() + } else if cfg.Db.IsPostgres() { + // postgres does not have unsigned data types + // https://www.postgresql.org/docs/10/datatype-numeric.html + tx := db.Begin() + if err := tx.Exec("ALTER TABLE heartbeats ALTER COLUMN id TYPE BIGINT").Error; err != nil { + return err + } + if err := tx.Exec("ALTER TABLE summary_items ALTER COLUMN id TYPE BIGINT").Error; err != nil { + return err + } + tx.Commit() + } else { + // sqlite doesn't allow for changing column type easily + // https://stackoverflow.com/a/2083562/3112139 + logbuch.Warn("unable to migrate id columns to bigint on %s", cfg.Db.Dialect) + } + + setHasRun(name, db) + return nil + }, + } + + registerPostMigration(f) +} diff --git a/migrations/shared.go b/migrations/shared.go new file mode 100644 index 0000000..7e5e29b --- /dev/null +++ b/migrations/shared.go @@ -0,0 +1,30 @@ +package migrations + +import ( + "github.com/emvi/logbuch" + "github.com/muety/wakapi/config" + "github.com/muety/wakapi/models" + "gorm.io/gorm" +) + +func hasRun(name string, db *gorm.DB) bool { + condition := "key = ?" + if config.Get().Db.Dialect == config.SQLDialectMysql { + condition = "`key` = ?" + } + lookupResult := db.Where(condition, name).First(&models.KeyStringValue{}) + if lookupResult.Error == nil && lookupResult.RowsAffected > 0 { + logbuch.Info("no need to migrate '%s'", name) + return true + } + return false +} + +func setHasRun(name string, db *gorm.DB) { + if err := db.Create(&models.KeyStringValue{ + Key: name, + Value: "done", + }).Error; err != nil { + logbuch.Error("failed to mark migration %s as run - %v", name, err) + } +} diff --git a/models/heartbeat.go b/models/heartbeat.go index 5571dc5..e683874 100644 --- a/models/heartbeat.go +++ b/models/heartbeat.go @@ -9,7 +9,7 @@ import ( ) type Heartbeat struct { - ID uint `gorm:"primary_key" hash:"ignore"` + ID uint64 `gorm:"primary_key" hash:"ignore"` User *User `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" hash:"ignore"` UserID string `json:"-" gorm:"not null; index:idx_time_user"` Entity string `json:"entity" gorm:"not null; index:idx_entity"` diff --git a/models/summary.go b/models/summary.go index 0407f0e..e9cfcc0 100644 --- a/models/summary.go +++ b/models/summary.go @@ -36,7 +36,7 @@ type Summary struct { type SummaryItems []*SummaryItem type SummaryItem struct { - ID uint `json:"-" gorm:"primary_key"` + ID uint64 `json:"-" gorm:"primary_key"` Summary *Summary `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` SummaryID uint `json:"-"` Type uint8 `json:"-" gorm:"index:idx_type"`