From d3901daffaa20b81ce34f54cd10d43c76864f692 Mon Sep 17 00:00:00 2001 From: Googol Lee Date: Wed, 24 Aug 2022 16:55:48 +0200 Subject: [PATCH] #5635 Add Config.DryRunMigration to prevent actually changing the schema when doing AutoMigrate() --- gorm.go | 12 +++++++---- migrator/migrator.go | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/gorm.go b/gorm.go index 1f1dac213..4c8ed8cce 100644 --- a/gorm.go +++ b/gorm.go @@ -31,6 +31,8 @@ type Config struct { NowFunc func() time.Time // DryRun generate sql without execute DryRun bool + // DryRunMigration runs AutoMigrate() without changing schema + DryRunMigration bool // PrepareStmt executes the given query in cached statement PrepareStmt bool // DisableAutomaticPing @@ -457,10 +459,12 @@ func (db *DB) Use(plugin Plugin) error { // ToSQL for generate SQL string. // // db.ToSQL(func(tx *gorm.DB) *gorm.DB { -// return tx.Model(&User{}).Where(&User{Name: "foo", Age: 20}) -// .Limit(10).Offset(5) -// .Order("name ASC") -// .First(&User{}) +// +// return tx.Model(&User{}).Where(&User{Name: "foo", Age: 20}) +// .Limit(10).Offset(5) +// .Order("name ASC") +// .First(&User{}) +// // }) func (db *DB) ToSQL(queryFn func(tx *DB) *DB) string { tx := queryFn(db.Session(&Session{DryRun: true, SkipDefaultTransaction: true})) diff --git a/migrator/migrator.go b/migrator/migrator.go index 87ac77451..07bcff931 100644 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -172,6 +172,10 @@ func (m Migrator) GetTables() (tableList []string, err error) { // CreateTable create table in database for values func (m Migrator) CreateTable(values ...interface{}) error { + if m.DB.DryRunMigration { + return fmt.Errorf("create table %#v: %w", values, gorm.ErrDryRunModeUnsupported) + } + for _, value := range m.ReorderModels(values, false) { tx := m.DB.Session(&gorm.Session{}) if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) { @@ -263,6 +267,10 @@ func (m Migrator) CreateTable(values ...interface{}) error { // DropTable drop table for values func (m Migrator) DropTable(values ...interface{}) error { + if m.DB.DryRunMigration { + return fmt.Errorf("drop table %#v: %w", values, gorm.ErrDryRunModeUnsupported) + } + values = m.ReorderModels(values, false) for i := len(values) - 1; i >= 0; i-- { tx := m.DB.Session(&gorm.Session{}) @@ -289,6 +297,10 @@ func (m Migrator) HasTable(value interface{}) bool { // RenameTable rename table from oldName to newName func (m Migrator) RenameTable(oldName, newName interface{}) error { + if m.DB.DryRunMigration { + return fmt.Errorf("rename table %s -> %s: %w", oldName, newName, gorm.ErrDryRunModeUnsupported) + } + var oldTable, newTable interface{} if v, ok := oldName.(string); ok { oldTable = clause.Table{Name: v} @@ -325,6 +337,10 @@ func (m Migrator) AddColumn(value interface{}, name string) error { } if !f.IgnoreMigration { + if m.DB.DryRunMigration { + return fmt.Errorf("add column %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + return m.DB.Exec( "ALTER TABLE ? ADD ? ?", m.CurrentTable(stmt), clause.Column{Name: f.DBName}, m.DB.Migrator().FullDataTypeOf(f), @@ -342,6 +358,10 @@ func (m Migrator) DropColumn(value interface{}, name string) error { name = field.DBName } + if m.DB.DryRunMigration { + return fmt.Errorf("drop column %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + return m.DB.Exec( "ALTER TABLE ? DROP COLUMN ?", m.CurrentTable(stmt), clause.Column{Name: name}, ).Error @@ -352,6 +372,10 @@ func (m Migrator) DropColumn(value interface{}, name string) error { func (m Migrator) AlterColumn(value interface{}, field string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { if field := stmt.Schema.LookUpField(field); field != nil { + if m.DB.DryRunMigration { + return fmt.Errorf("alter column %s in table %s: %w", field.Name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + fileType := m.FullDataTypeOf(field) return m.DB.Exec( "ALTER TABLE ? ALTER COLUMN ? TYPE ?", @@ -393,6 +417,10 @@ func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error newName = field.DBName } + if m.DB.DryRunMigration { + return fmt.Errorf("rename column %s -> %s in table %s: %w", oldName, newName, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + return m.DB.Exec( "ALTER TABLE ? RENAME COLUMN ? TO ?", m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName}, @@ -478,6 +506,9 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy } if alterColumn && !field.IgnoreMigration { + if m.DB.DryRunMigration { + return fmt.Errorf("migrate column %s in table %T: %w", field.Name, value, gorm.ErrDryRunModeUnsupported) + } return m.DB.Migrator().AlterColumn(value, field.Name) } @@ -593,6 +624,10 @@ func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_ // CreateConstraint create constraint func (m Migrator) CreateConstraint(value interface{}, name string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if m.DB.DryRunMigration { + return fmt.Errorf("create constraint %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) if chk != nil { return m.DB.Exec( @@ -617,6 +652,10 @@ func (m Migrator) CreateConstraint(value interface{}, name string) error { // DropConstraint drop constraint func (m Migrator) DropConstraint(value interface{}, name string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if m.DB.DryRunMigration { + return fmt.Errorf("drop constraint %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) if constraint != nil { name = constraint.Name @@ -679,6 +718,10 @@ type BuildIndexOptionsInterface interface { func (m Migrator) CreateIndex(value interface{}, name string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { if idx := stmt.Schema.LookIndex(name); idx != nil { + if m.DB.DryRunMigration { + return fmt.Errorf("create index %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt) values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts} @@ -710,6 +753,10 @@ func (m Migrator) CreateIndex(value interface{}, name string) error { // DropIndex drop index `name` func (m Migrator) DropIndex(value interface{}, name string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if m.DB.DryRunMigration { + return fmt.Errorf("drop index %s in table %s: %w", name, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + if idx := stmt.Schema.LookIndex(name); idx != nil { name = idx.Name } @@ -739,6 +786,10 @@ func (m Migrator) HasIndex(value interface{}, name string) bool { // RenameIndex rename index from oldName to newName func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if m.DB.DryRunMigration { + return fmt.Errorf("rename index %s -> %s in table %s: %w", oldName, newName, m.CurrentTable(stmt), gorm.ErrDryRunModeUnsupported) + } + return m.DB.Exec( "ALTER TABLE ? RENAME INDEX ? TO ?", m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},