add MigrateColumnUnique to Migrator interface

This commit is contained in:
black 2023-06-19 19:32:29 +08:00
parent 56eb4be78e
commit c7db4af98c
2 changed files with 20 additions and 81 deletions

View File

@ -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)

View File

@ -26,8 +26,6 @@ type Migrator struct {
// Config schema config // Config schema config
type Config struct { type Config struct {
CreateIndexAfterCreateTable bool CreateIndexAfterCreateTable bool
// Unique in ColumnType is affected by UniqueIndex, e.g. MySQL
UniqueAffectedByUniqueIndex bool
DB *gorm.DB DB *gorm.DB
gorm.Dialector gorm.Dialector
} }
@ -97,15 +95,20 @@ func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
return return
} }
func (m Migrator) GetQueryAndExecTx() (queryTx, execTx *gorm.DB) {
queryTx = m.DB.Session(&gorm.Session{})
execTx = queryTx
if m.DB.DryRun {
queryTx.DryRun = false
execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
}
return queryTx, execTx
}
// AutoMigrate auto migrate values // AutoMigrate auto migrate values
func (m Migrator) AutoMigrate(values ...interface{}) error { func (m Migrator) AutoMigrate(values ...interface{}) error {
for _, value := range m.ReorderModels(values, true) { for _, value := range m.ReorderModels(values, true) {
queryTx := m.DB.Session(&gorm.Session{}) queryTx, execTx := m.GetQueryAndExecTx()
execTx := queryTx
if m.DB.DryRun {
queryTx.DryRun = false
execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
}
if !queryTx.Migrator().HasTable(value) { if !queryTx.Migrator().HasTable(value) {
if err := execTx.Migrator().CreateTable(value); err != nil { if err := execTx.Migrator().CreateTable(value); err != nil {
return err return err
@ -528,7 +531,7 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
} }
} }
if err := m.MigrateColumnUnique(value, field, columnType); err != nil { if err := m.DB.Migrator().MigrateColumnUnique(value, field, columnType); err != nil {
return err return err
} }
@ -540,82 +543,16 @@ func (m Migrator) MigrateColumnUnique(value interface{}, field *schema.Field, co
if !ok || field.PrimaryKey { if !ok || field.PrimaryKey {
return nil // skip primary key return nil // skip primary key
} }
// By default, ColumnType's Unique is not affected by UniqueIndex, so we don't care about UniqueIndex.
queryTx := m.DB.Session(&gorm.Session{})
execTx := queryTx
if m.DB.DryRun {
queryTx.DryRun = false
execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
}
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, // We're currently only receiving boolean values on `Unique` tag,
// so the UniqueConstraint name is fixed // so the UniqueConstraint name is fixed
constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName) constraint := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
if m.UniqueAffectedByUniqueIndex { if unique && !field.Unique {
if unique { return m.DB.Migrator().DropConstraint(value, constraint)
// Clean up redundant unique indexes }
indexes, _ := queryTx.Migrator().GetIndexes(value) if !unique && field.Unique {
for _, index := range indexes { return m.DB.Migrator().CreateConstraint(value, constraint)
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)
switch {
case field.Unique && !hasConstraint:
if field.Unique {
if err := execTx.Migrator().CreateConstraint(value, constraint); err != nil {
return err
}
}
// field isn't Unique but ColumnType's Unique is reported by UniqueConstraint.
case !field.Unique && hasConstraint:
if err := execTx.Migrator().DropConstraint(value, constraint); err != nil {
return err
}
if field.UniqueIndex != "" {
if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
return err
}
}
}
if field.UniqueIndex != "" && !queryTx.Migrator().HasIndex(value, field.UniqueIndex) {
if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
return err
}
}
} else {
if field.Unique {
if err := execTx.Migrator().CreateConstraint(value, constraint); err != nil {
return err
}
}
if field.UniqueIndex != "" {
if err := execTx.Migrator().CreateIndex(value, field.UniqueIndex); err != nil {
return err
}
}
return nil
}
} else {
if unique && !field.Unique {
return execTx.Migrator().DropConstraint(value, constraint)
}
if !unique && field.Unique {
return execTx.Migrator().CreateConstraint(value, constraint)
}
} }
return nil return nil
}) })