Skip to content

Commit

Permalink
#5635 when setting Config.DryRunMigration, AutoMigrate() returns an e…
Browse files Browse the repository at this point in the history
…rror before changing schemas.
  • Loading branch information
googollee committed Sep 15, 2022
1 parent edb00c1 commit 9d4ea51
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 4 deletions.
17 changes: 13 additions & 4 deletions gorm.go
Expand Up @@ -31,6 +31,8 @@ type Config struct {
NowFunc func() time.Time
// DryRun generate sql without execute
DryRun bool
// DryRunMigration prevent AutoMigrate() to change the schema
DryRunMigration bool
// PrepareStmt executes the given query in cached statement
PrepareStmt bool
// DisableAutomaticPing
Expand Down Expand Up @@ -97,6 +99,7 @@ type DB struct {
// Session session config when create session with Session() method
type Session struct {
DryRun bool
DryRunMigration bool
PrepareStmt bool
NewDB bool
Initialized bool
Expand Down Expand Up @@ -274,6 +277,10 @@ func (db *DB) Session(config *Session) *DB {
tx.Config.DryRun = true
}

if config.DryRunMigration {
tx.Config.DryRunMigration = true
}

if config.QueryFields {
tx.Config.QueryFields = true
}
Expand Down Expand Up @@ -457,10 +464,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}))
Expand Down
24 changes: 24 additions & 0 deletions migrator/migrator.go
Expand Up @@ -94,6 +94,10 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
for _, value := range m.ReorderModels(values, true) {
tx := m.DB.Session(&gorm.Session{})
if !tx.Migrator().HasTable(value) {
if tx.DryRunMigration {
return fmt.Errorf("create table for model %T: %w", value, gorm.ErrDryRunModeUnsupported)
}

if err := tx.Migrator().CreateTable(value); err != nil {
return err
}
Expand All @@ -117,6 +121,10 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {

if foundColumn == nil {
// not found, add column
if tx.DryRunMigration {
return fmt.Errorf("create column for model %T: %w", value, gorm.ErrDryRunModeUnsupported)
}

if err := tx.Migrator().AddColumn(value, dbName); err != nil {
return err
}
Expand All @@ -130,6 +138,10 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
if !m.DB.Config.DisableForeignKeyConstraintWhenMigrating {
if constraint := rel.ParseConstraint(); constraint != nil &&
constraint.Schema == stmt.Schema && !tx.Migrator().HasConstraint(value, constraint.Name) {
if tx.DryRunMigration {
return fmt.Errorf("create constraint %s for model %T: %w", constraint.Name, value, gorm.ErrDryRunModeUnsupported)
}

if err := tx.Migrator().CreateConstraint(value, constraint.Name); err != nil {
return err
}
Expand All @@ -139,6 +151,10 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {

for _, chk := range stmt.Schema.ParseCheckConstraints() {
if !tx.Migrator().HasConstraint(value, chk.Name) {
if tx.DryRunMigration {
return fmt.Errorf("create constraint %s for model %T: %w", chk.Name, value, gorm.ErrDryRunModeUnsupported)
}

if err := tx.Migrator().CreateConstraint(value, chk.Name); err != nil {
return err
}
Expand All @@ -147,6 +163,10 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {

for _, idx := range stmt.Schema.ParseIndexes() {
if !tx.Migrator().HasIndex(value, idx.Name) {
if tx.DryRunMigration {
return fmt.Errorf("create index %s for model %T: %w", idx.Name, value, gorm.ErrDryRunModeUnsupported)
}

if err := tx.Migrator().CreateIndex(value, idx.Name); err != nil {
return err
}
Expand Down Expand Up @@ -478,6 +498,10 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
}

if alterColumn && !field.IgnoreMigration {
if m.DB.DryRunMigration {
return fmt.Errorf("alter column %s for model %T: %w", field.Name, value, gorm.ErrDryRunModeUnsupported)
}

return m.DB.Migrator().AlterColumn(value, field.Name)
}

Expand Down
62 changes: 62 additions & 0 deletions tests/migrate_test.go
@@ -1,6 +1,7 @@
package tests_test

import (
"errors"
"fmt"
"math/rand"
"reflect"
Expand Down Expand Up @@ -959,3 +960,64 @@ func TestMigrateArrayTypeModel(t *testing.T) {
AssertEqual(t, nil, err)
AssertEqual(t, "integer[]", ct.DatabaseTypeName())
}

type Origin struct {
ID int64 `gorm:"primaryKey"`
Data string `gorm:"null"`
}

func (Origin) TableName() string {
return "test"
}

func TestDryRunAutoMigrate(t *testing.T) {
type ChangeColumn struct {
Origin `gorm:"-"`
ID int64 `gorm:"primaryKey"`
Data int64 `gorm:""`
}

type AddIndex struct {
Origin `gorm:"-"`
ID int64 `gorm:"primaryKey"`
Data string `gorm:"null;index"`
}

type AddConstraint struct {
Origin `gorm:"-"`
ID int64 `gorm:"primaryKey"`
Data string `gorm:"null;check:,data <> 'migrate'"`
}

var tests = []struct {
from, to interface{}
dryrunErr bool
}{
{&Origin{}, &Origin{}, false},
{&Origin{}, &ChangeColumn{}, true},
{&Origin{}, &AddIndex{}, true},
{&Origin{}, &AddConstraint{}, true},
}

for _, test := range tests {
name := strings.ReplaceAll(fmt.Sprintf("%T", test.to), "*", "")
t.Run(name, func(t *testing.T) {
DB.Migrator().DropTable(test.from, test.to)
t.Cleanup(func() {
DB.Migrator().DropTable(test.from, test.to)
})

err := DB.Migrator().CreateTable(test.from)
AssertEqual(t, nil, err)

err = DB.Session(&gorm.Session{DryRunMigration: true}).AutoMigrate(test.to)
if err != nil {
t.Log("migrate error:", err)
}
AssertEqual(t, test.dryrunErr, errors.Is(err, gorm.ErrDryRunModeUnsupported))

err = DB.AutoMigrate(test.to)
AssertEqual(t, false, errors.Is(err, gorm.ErrDryRunModeUnsupported))
})
}
}

0 comments on commit 9d4ea51

Please sign in to comment.