mirror of https://github.com/go-gorm/gorm.git
feat: add MigrateColumnUnique (#6640)
* feat: add MigrateColumnUnique * feat: define new methods * delete debug in test
This commit is contained in:
parent
5adc0ce5f6
commit
9fea15ae75
|
@ -87,6 +87,8 @@ type Migrator interface {
|
||||||
DropColumn(dst interface{}, field string) error
|
DropColumn(dst interface{}, field string) error
|
||||||
AlterColumn(dst interface{}, field string) error
|
AlterColumn(dst interface{}, field string) error
|
||||||
MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
|
MigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType) error
|
||||||
|
// MigrateColumnUnique migrate column's UNIQUE constraint, it's part of MigrateColumn.
|
||||||
|
MigrateColumnUnique(dst interface{}, field *schema.Field, columnType ColumnType) error
|
||||||
HasColumn(dst interface{}, field string) bool
|
HasColumn(dst interface{}, field string) bool
|
||||||
RenameColumn(dst interface{}, oldName, field string) error
|
RenameColumn(dst interface{}, oldName, field string) error
|
||||||
ColumnTypes(dst interface{}) ([]ColumnType, error)
|
ColumnTypes(dst interface{}) ([]ColumnType, error)
|
||||||
|
|
|
@ -27,6 +27,8 @@ var regFullDataType = regexp.MustCompile(`\D*(\d+)\D?`)
|
||||||
|
|
||||||
// TODO:? Create const vars for raw sql queries ?
|
// TODO:? Create const vars for raw sql queries ?
|
||||||
|
|
||||||
|
var _ gorm.Migrator = (*Migrator)(nil)
|
||||||
|
|
||||||
// Migrator m struct
|
// Migrator m struct
|
||||||
type Migrator struct {
|
type Migrator struct {
|
||||||
Config
|
Config
|
||||||
|
@ -539,6 +541,26 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
|
||||||
|
unique, ok := columnType.Unique()
|
||||||
|
if !ok || field.PrimaryKey {
|
||||||
|
return nil // skip primary key
|
||||||
|
}
|
||||||
|
// By default, ColumnType's Unique is not affected by UniqueIndex, so we don't care about UniqueIndex.
|
||||||
|
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)
|
||||||
|
if unique && !field.Unique {
|
||||||
|
return m.DB.Migrator().DropConstraint(value, constraint)
|
||||||
|
}
|
||||||
|
if !unique && field.Unique {
|
||||||
|
return m.DB.Migrator().CreateConstraint(value, constraint)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ColumnTypes return columnTypes []gorm.ColumnType and execErr error
|
// ColumnTypes return columnTypes []gorm.ColumnType and execErr error
|
||||||
func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
|
func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
|
||||||
columnTypes := make([]gorm.ColumnType, 0)
|
columnTypes := make([]gorm.ColumnType, 0)
|
||||||
|
|
|
@ -19,6 +19,7 @@ type Namer interface {
|
||||||
RelationshipFKName(Relationship) string
|
RelationshipFKName(Relationship) string
|
||||||
CheckerName(table, column string) string
|
CheckerName(table, column string) string
|
||||||
IndexName(table, column string) string
|
IndexName(table, column string) string
|
||||||
|
UniqueName(table, column string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replacer replacer interface like strings.Replacer
|
// Replacer replacer interface like strings.Replacer
|
||||||
|
@ -26,6 +27,8 @@ type Replacer interface {
|
||||||
Replace(name string) string
|
Replace(name string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ Namer = (*NamingStrategy)(nil)
|
||||||
|
|
||||||
// NamingStrategy tables, columns naming strategy
|
// NamingStrategy tables, columns naming strategy
|
||||||
type NamingStrategy struct {
|
type NamingStrategy struct {
|
||||||
TablePrefix string
|
TablePrefix string
|
||||||
|
@ -85,6 +88,11 @@ func (ns NamingStrategy) IndexName(table, column string) string {
|
||||||
return ns.formatName("idx", table, ns.toDBName(column))
|
return ns.formatName("idx", table, ns.toDBName(column))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniqueName generate unique constraint name
|
||||||
|
func (ns NamingStrategy) UniqueName(table, column string) string {
|
||||||
|
return ns.formatName("uni", table, ns.toDBName(column))
|
||||||
|
}
|
||||||
|
|
||||||
func (ns NamingStrategy) formatName(prefix, table, name string) string {
|
func (ns NamingStrategy) formatName(prefix, table, name string) string {
|
||||||
formattedName := strings.ReplaceAll(strings.Join([]string{
|
formattedName := strings.ReplaceAll(strings.Join([]string{
|
||||||
prefix, table, name,
|
prefix, table, name,
|
||||||
|
|
|
@ -278,8 +278,6 @@ func TestBelongsToAssociationUnscoped(t *testing.T) {
|
||||||
t.Fatalf("failed to create items, got error: %v", err)
|
t.Fatalf("failed to create items, got error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = tx.Debug()
|
|
||||||
|
|
||||||
// test replace
|
// test replace
|
||||||
if err := tx.Model(&item).Association("ItemParent").Unscoped().Replace(&ItemParent{
|
if err := tx.Model(&item).Association("ItemParent").Unscoped().Replace(&ItemParent{
|
||||||
Logo: "updated logo",
|
Logo: "updated logo",
|
||||||
|
|
|
@ -29,7 +29,7 @@ func TestCountWithGroup(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var count2 int64
|
var count2 int64
|
||||||
if err := DB.Debug().Model(&Company{}).Where("name in ?", []string{"company_count_group_b", "company_count_group_c"}).Group("name").Count(&count2).Error; err != nil {
|
if err := DB.Model(&Company{}).Where("name in ?", []string{"company_count_group_b", "company_count_group_c"}).Group("name").Count(&count2).Error; err != nil {
|
||||||
t.Errorf(fmt.Sprintf("Count should work, but got err %v", err))
|
t.Errorf(fmt.Sprintf("Count should work, but got err %v", err))
|
||||||
}
|
}
|
||||||
if count2 != 2 {
|
if count2 != 2 {
|
||||||
|
|
|
@ -429,7 +429,6 @@ func TestEmbedPreload(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
DB = DB.Debug()
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
actual := Org{}
|
actual := Org{}
|
||||||
|
|
|
@ -838,7 +838,7 @@ func TestSaveWithHooks(t *testing.T) {
|
||||||
saveTokenOwner := func(owner *TokenOwner) (*TokenOwner, error) {
|
saveTokenOwner := func(owner *TokenOwner) (*TokenOwner, error) {
|
||||||
var newOwner TokenOwner
|
var newOwner TokenOwner
|
||||||
if err := DB.Transaction(func(tx *gorm.DB) error {
|
if err := DB.Transaction(func(tx *gorm.DB) error {
|
||||||
if err := tx.Debug().Session(&gorm.Session{FullSaveAssociations: true}).Save(owner).Error; err != nil {
|
if err := tx.Session(&gorm.Session{FullSaveAssociations: true}).Save(owner).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := tx.Preload("Token").First(&newOwner, owner.ID).Error; err != nil {
|
if err := tx.Preload("Token").First(&newOwner, owner.ID).Error; err != nil {
|
||||||
|
|
Loading…
Reference in New Issue