fix MigrateColumnUnique

This commit is contained in:
black 2023-06-14 21:08:11 +08:00
parent a9e2dfc503
commit 1529430536
5 changed files with 62 additions and 34 deletions

View File

@ -549,11 +549,28 @@ func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, co
} }
return m.RunWithValue(value, func(stmt *gorm.Statement) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error {
// We're currently only receiving boolean values on `Unique` tag,
// so the UniqueConstraint name is fixed
constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName) constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
index := m.DB.NamingStrategy.IndexName(stmt.Table, field.DBName)
if m.UniqueAffectedByUniqueIndex { if m.UniqueAffectedByUniqueIndex {
if unique { if unique {
// Clean up redundant unique indexes
indexes, _ := queryTx.Migrator().GetIndexes(value)
for _, index := range indexes {
if uni, ok := index.Unique(); !ok || !uni {
continue
}
if columns := index.Columns(); len(columns) != 1 || columns[0] != field.DBName {
continue
}
if name := index.Name(); name == constraint || name == field.UniqueIndex {
continue
}
if err := execTx.Migrator().DropIndex(value, index.Name()); err != nil {
return err
}
}
hasConstraint := queryTx.Migrator().HasConstraint(value, constraint) hasConstraint := queryTx.Migrator().HasConstraint(value, constraint)
switch { switch {
case field.Unique && !hasConstraint: case field.Unique && !hasConstraint:
@ -567,30 +584,17 @@ func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, co
if err := execTx.Migrator().DropConstraint(value, constraint); err != nil { if err := execTx.Migrator().DropConstraint(value, constraint); err != nil {
return err return err
} }
if field.UniqueIndex { if field.UniqueIndex != "" {
if err := execTx.Migrator().CreateIndex(value, index); err != nil { if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
return err return err
} }
} }
} }
hasIndex := queryTx.Migrator().HasIndex(value, index) if field.UniqueIndex != "" && !queryTx.Migrator().HasIndex(value, field.UniqueIndex) {
switch { if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
case field.UniqueIndex && !hasIndex:
if err := execTx.Migrator().CreateIndex(value, index); err != nil {
return err return err
} }
// field isn't UniqueIndex but ColumnType's Unique is reported by UniqueIndex
case !field.UniqueIndex && hasIndex:
if err := execTx.Migrator().DropIndex(value, index); err != nil {
return err
}
// create normal index
if idx := stmt.Schema.LookIndex(index); idx != nil {
if err := execTx.Migrator().CreateIndex(value, index); err != nil {
return err
}
}
} }
} else { } else {
if field.Unique { if field.Unique {
@ -598,8 +602,8 @@ func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, co
return err return err
} }
} }
if field.UniqueIndex { if field.UniqueIndex != "" {
if err := execTx.Migrator().CreateIndex(value, index); err != nil { if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
return err return err
} }
} }

View File

@ -69,7 +69,7 @@ type Field struct {
DefaultValueInterface interface{} DefaultValueInterface interface{}
NotNull bool NotNull bool
Unique bool Unique bool
UniqueIndex bool UniqueIndex string
Comment string Comment string
Size int Size int
Precision int Precision int

View File

@ -67,7 +67,7 @@ func (schema *Schema) ParseIndexes() map[string]Index {
} }
for _, index := range indexes { for _, index := range indexes {
if index.Class == "UNIQUE" && len(index.Fields) == 1 { if index.Class == "UNIQUE" && len(index.Fields) == 1 {
index.Fields[0].Field.UniqueIndex = true index.Fields[0].Field.UniqueIndex = index.Name
} }
} }
return indexes return indexes

View File

@ -66,7 +66,7 @@ func TestParseIndex(t *testing.T) {
"idx_name": { "idx_name": {
Name: "idx_name", Name: "idx_name",
Class: "UNIQUE", Class: "UNIQUE",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name2", UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name2", UniqueIndex: "idx_name"}}},
}, },
"idx_user_indices_name3": { "idx_user_indices_name3": {
Name: "idx_user_indices_name3", Name: "idx_user_indices_name3",
@ -82,7 +82,7 @@ func TestParseIndex(t *testing.T) {
"idx_user_indices_name4": { "idx_user_indices_name4": {
Name: "idx_user_indices_name4", Name: "idx_user_indices_name4",
Class: "UNIQUE", Class: "UNIQUE",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name4", UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name4", UniqueIndex: "idx_user_indices_name4"}}},
}, },
"idx_user_indices_name5": { "idx_user_indices_name5": {
Name: "idx_user_indices_name5", Name: "idx_user_indices_name5",
@ -103,12 +103,12 @@ func TestParseIndex(t *testing.T) {
}, },
"idx_id": { "idx_id": {
Name: "idx_id", Name: "idx_id",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "MemberNumber"}}, {Field: &schema.Field{Name: "OID", UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "MemberNumber"}}, {Field: &schema.Field{Name: "OID", UniqueIndex: "idx_oid"}}},
}, },
"idx_oid": { "idx_oid": {
Name: "idx_oid", Name: "idx_oid",
Class: "UNIQUE", Class: "UNIQUE",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "OID", UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "OID", UniqueIndex: "idx_oid"}}},
}, },
"type": { "type": {
Name: "type", Name: "type",
@ -191,7 +191,7 @@ func TestParseIndexWithUniqueIndexAndUnique(t *testing.T) {
"idx_index_tests_field_c": { "idx_index_tests_field_c": {
Name: "idx_index_tests_field_c", Name: "idx_index_tests_field_c",
Class: "UNIQUE", Class: "UNIQUE",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldC", UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldC", UniqueIndex: "idx_index_tests_field_c"}}},
}, },
"idx_index_tests_field_d": { "idx_index_tests_field_d": {
Name: "idx_index_tests_field_d", Name: "idx_index_tests_field_d",
@ -225,7 +225,7 @@ func TestParseIndexWithUniqueIndexAndUnique(t *testing.T) {
"idx_index_tests_field_g": { "idx_index_tests_field_g": {
Name: "idx_index_tests_field_g", Name: "idx_index_tests_field_g",
Class: "UNIQUE", Class: "UNIQUE",
Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldG", Unique: true, UniqueIndex: true}}}, Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldG", Unique: true, UniqueIndex: "idx_index_tests_field_g"}}},
}, },
"uniq_field_h1_h2": { "uniq_field_h1_h2": {
Name: "uniq_field_h1_h2", Name: "uniq_field_h1_h2",

View File

@ -15,6 +15,7 @@ import (
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
"gorm.io/gorm/migrator" "gorm.io/gorm/migrator"
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
@ -925,7 +926,8 @@ func TestCurrentTimestamp(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("AutoMigrate err:%v", err) t.Fatalf("AutoMigrate err:%v", err)
} }
AssertEqual(t, true, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at")) AssertEqual(t, true, DB.Migrator().HasConstraint(&CurrentTimestampTest{}, "uni_current_timestamp_tests_time_at"))
AssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at"))
AssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at_2")) AssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at_2"))
} }
@ -987,7 +989,8 @@ func TestUniqueColumn(t *testing.T) {
} }
// not trigger alert column // not trigger alert column
AssertEqual(t, true, DB.Migrator().HasIndex(&UniqueTest{}, "name")) AssertEqual(t, true, DB.Migrator().HasConstraint(&UniqueTest{}, "uni_unique_tests_name"))
AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name"))
AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_1")) AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_1"))
AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_2")) AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_2"))
@ -1648,13 +1651,12 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
if field.Unique != unique { if field.Unique != unique {
t.Fatalf("%v: %q column %q unique should be %v but got %v", utils.FileWithLineNum(), stmt.Schema.Table, fieldName, unique, field.Unique) t.Fatalf("%v: %q column %q unique should be %v but got %v", utils.FileWithLineNum(), stmt.Schema.Table, fieldName, unique, field.Unique)
} }
if field.UniqueIndex != uniqueIndex { if (field.UniqueIndex != "") != uniqueIndex {
t.Fatalf("%v: %q column %q uniqueIndex should be %v but got %v", utils.FileWithLineNum(), stmt.Schema, fieldName, uniqueIndex, field.UniqueIndex) t.Fatalf("%v: %q column %q uniqueIndex should be %v but got %v", utils.FileWithLineNum(), stmt.Schema, fieldName, uniqueIndex, field.UniqueIndex)
} }
} }
// not unique type ( // not unique
type (
UniqueStruct1 struct { UniqueStruct1 struct {
Name string `gorm:"size:10"` Name string `gorm:"size:10"`
} }
@ -1774,4 +1776,26 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
test.checkFunc(t) test.checkFunc(t)
}) })
} }
if DB.Dialector.Name() == "mysql" {
compatibilityTests := []TestCase{
{name: "oldUnique to notUnique", to: UniqueStruct1{}, checkFunc: checkNotUnique},
{name: "oldUnique to unique", to: UniqueStruct3{}, checkFunc: checkUnique},
{name: "oldUnique to uniqueIndex", to: UniqueStruct5{}, checkFunc: checkUniqueIndex},
}
for _, test := range compatibilityTests {
t.Run(test.name, func(t *testing.T) {
if err := DB.Migrator().DropTable(table); err != nil {
t.Fatalf("failed to drop table, got error: %v", err)
}
if err := DB.Exec("CREATE TABLE ? (`name` varchar(10) UNIQUE)", clause.Table{Name: table}).Error; err != nil {
t.Fatalf("failed to create table, got error: %v", err)
}
if err := DB.Debug().Table(table).AutoMigrate(test.to); err != nil {
t.Fatalf("failed to migrate table, got error: %v", err)
}
test.checkFunc(t)
})
}
}
} }