Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: auto migration column order unpredictable #4980

Merged
merged 1 commit into from Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions migrator/migrator.go
Expand Up @@ -97,19 +97,20 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
columnTypes, _ := m.DB.Migrator().ColumnTypes(value)

for _, field := range stmt.Schema.FieldsByDBName {
for _, dbName := range stmt.Schema.DBNames {
field := stmt.Schema.FieldsByDBName[dbName]
var foundColumn gorm.ColumnType

for _, columnType := range columnTypes {
if columnType.Name() == field.DBName {
if columnType.Name() == dbName {
foundColumn = columnType
break
}
}

if foundColumn == nil {
// not found, add column
if err := tx.Migrator().AddColumn(value, field.DBName); err != nil {
if err := tx.Migrator().AddColumn(value, dbName); err != nil {
return err
}
} else if err := m.DB.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
Expand Down
72 changes: 72 additions & 0 deletions tests/migrate_test.go
Expand Up @@ -2,11 +2,13 @@ package tests_test

import (
"math/rand"
"reflect"
"strings"
"testing"
"time"

"gorm.io/gorm"
"gorm.io/gorm/schema"
. "gorm.io/gorm/utils/tests"
)

Expand Down Expand Up @@ -455,3 +457,73 @@ func TestMigrateIndexesWithDynamicTableName(t *testing.T) {
}
}
}

// check column order after migration, flaky test
// https://github.com/go-gorm/gorm/issues/4351
func TestMigrateColumnOrder(t *testing.T) {
type UserMigrateColumn struct {
ID uint
}
DB.Migrator().DropTable(&UserMigrateColumn{})
DB.AutoMigrate(&UserMigrateColumn{})

type UserMigrateColumn2 struct {
ID uint
F1 string
F2 string
F3 string
F4 string
F5 string
F6 string
F7 string
F8 string
F9 string
F10 string
F11 string
F12 string
F13 string
F14 string
F15 string
F16 string
F17 string
F18 string
F19 string
F20 string
F21 string
F22 string
F23 string
F24 string
F25 string
F26 string
F27 string
F28 string
F29 string
F30 string
F31 string
F32 string
F33 string
F34 string
F35 string
}
if err := DB.Table("user_migrate_columns").AutoMigrate(&UserMigrateColumn2{}); err != nil {
t.Fatalf("failed to auto migrate, got error: %v", err)
}

columnTypes, err := DB.Table("user_migrate_columns").Migrator().ColumnTypes(&UserMigrateColumn2{})
if err != nil {
t.Fatalf("failed to get column types, got error: %v", err)
}
typ := reflect.Indirect(reflect.ValueOf(&UserMigrateColumn2{})).Type()
numField := typ.NumField()
if numField != len(columnTypes) {
t.Fatalf("column's number not match struct and ddl, %d != %d", numField, len(columnTypes))
}
namer := schema.NamingStrategy{}
for i := 0; i < numField; i++ {
expectName := namer.ColumnName("", typ.Field(i).Name)
if columnTypes[i].Name() != expectName {
t.Fatalf("column order not match struct and ddl, idx %d: %s != %s",
i, columnTypes[i].Name(), expectName)
}
}
}