forked from mirror/gorm
Test migrate constraints, close #3986
This commit is contained in:
parent
59c01b7943
commit
916338a9e1
|
@ -451,50 +451,80 @@ func buildConstraint(constraint *schema.Constraint) (sql string, results []inter
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_ *schema.Constraint, _ *schema.Check, table string) {
|
||||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
if stmt.Schema == nil {
|
||||||
|
return nil, nil, stmt.Table
|
||||||
|
}
|
||||||
|
|
||||||
checkConstraints := stmt.Schema.ParseCheckConstraints()
|
checkConstraints := stmt.Schema.ParseCheckConstraints()
|
||||||
if chk, ok := checkConstraints[name]; ok {
|
if chk, ok := checkConstraints[name]; ok {
|
||||||
|
return nil, &chk, stmt.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
getTable := func(rel *schema.Relationship) string {
|
||||||
|
switch rel.Type {
|
||||||
|
case schema.HasOne, schema.HasMany:
|
||||||
|
return rel.FieldSchema.Table
|
||||||
|
case schema.Many2Many:
|
||||||
|
return rel.JoinTable.Table
|
||||||
|
}
|
||||||
|
return stmt.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rel := range stmt.Schema.Relationships.Relations {
|
||||||
|
if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
|
||||||
|
return constraint, nil, getTable(rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if field := stmt.Schema.LookUpField(name); field != nil {
|
||||||
|
for _, cc := range checkConstraints {
|
||||||
|
if cc.Field == field {
|
||||||
|
return nil, &cc, stmt.Table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rel := range stmt.Schema.Relationships.Relations {
|
||||||
|
if constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {
|
||||||
|
return constraint, nil, getTable(rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
||||||
|
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
|
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
||||||
|
if chk != nil {
|
||||||
return m.DB.Exec(
|
return m.DB.Exec(
|
||||||
"ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
|
"ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
|
||||||
m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
|
m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
|
||||||
).Error
|
).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rel := range stmt.Schema.Relationships.Relations {
|
if constraint != nil {
|
||||||
if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
|
var vars = []interface{}{clause.Table{Name: table}}
|
||||||
|
if stmt.TableExpr != nil {
|
||||||
|
vars[0] = stmt.TableExpr
|
||||||
|
}
|
||||||
sql, values := buildConstraint(constraint)
|
sql, values := buildConstraint(constraint)
|
||||||
return m.DB.Exec("ALTER TABLE ? ADD "+sql, append([]interface{}{m.CurrentTable(stmt)}, values...)...).Error
|
return m.DB.Exec("ALTER TABLE ? ADD "+sql, append(vars, values...)...).Error
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := fmt.Errorf("failed to create constraint with name %v", name)
|
return nil
|
||||||
if field := stmt.Schema.LookUpField(name); field != nil {
|
|
||||||
for _, cc := range checkConstraints {
|
|
||||||
if err = m.DB.Migrator().CreateIndex(value, cc.Name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rel := range stmt.Schema.Relationships.Relations {
|
|
||||||
if constraint := rel.ParseConstraint(); constraint != nil && constraint.Field == field {
|
|
||||||
if err = m.DB.Migrator().CreateIndex(value, constraint.Name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Migrator) DropConstraint(value interface{}, name string) error {
|
func (m Migrator) DropConstraint(value interface{}, name string) error {
|
||||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
return m.DB.Exec(
|
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
||||||
"ALTER TABLE ? DROP CONSTRAINT ?",
|
if constraint != nil {
|
||||||
m.CurrentTable(stmt), clause.Column{Name: name},
|
name = constraint.Name
|
||||||
).Error
|
} else if chk != nil {
|
||||||
|
name = chk.Name
|
||||||
|
}
|
||||||
|
return m.DB.Exec("ALTER TABLE ? DROP CONSTRAINT ?", clause.Table{Name: table}, clause.Column{Name: name}).Error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,9 +532,16 @@ func (m Migrator) HasConstraint(value interface{}, name string) bool {
|
||||||
var count int64
|
var count int64
|
||||||
m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
currentDatabase := m.DB.Migrator().CurrentDatabase()
|
currentDatabase := m.DB.Migrator().CurrentDatabase()
|
||||||
|
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
||||||
|
if constraint != nil {
|
||||||
|
name = constraint.Name
|
||||||
|
} else if chk != nil {
|
||||||
|
name = chk.Name
|
||||||
|
}
|
||||||
|
|
||||||
return m.DB.Raw(
|
return m.DB.Raw(
|
||||||
"SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
|
"SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
|
||||||
currentDatabase, stmt.Table, name,
|
currentDatabase, table, name,
|
||||||
).Row().Scan(&count)
|
).Row().Scan(&count)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -519,7 +519,7 @@ func (rel *Relationship) ParseConstraint() *Constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ref := range rel.References {
|
for _, ref := range rel.References {
|
||||||
if ref.PrimaryKey != nil {
|
if ref.PrimaryKey != nil && (rel.JoinTable == nil || ref.OwnPrimaryKey) {
|
||||||
constraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey)
|
constraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey)
|
||||||
constraint.References = append(constraint.References, ref.PrimaryKey)
|
constraint.References = append(constraint.References, ref.PrimaryKey)
|
||||||
|
|
||||||
|
@ -533,10 +533,6 @@ func (rel *Relationship) ParseConstraint() *Constraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rel.JoinTable != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &constraint
|
return &constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -323,3 +323,33 @@ func TestMigrateColumns(t *testing.T) {
|
||||||
t.Fatalf("Found deleted column")
|
t.Fatalf("Found deleted column")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMigrateConstraint(t *testing.T) {
|
||||||
|
if DB.Dialector.Name() == "sqlite" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
names := []string{"Account", "fk_users_account", "Pets", "fk_users_pets", "Company", "fk_users_company", "Manager", "fk_users_manager", "Team", "fk_users_team", "Languages", "fk_users_languages"}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
if !DB.Migrator().HasConstraint(&User{}, name) {
|
||||||
|
DB.Migrator().CreateConstraint(&User{}, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DB.Migrator().DropConstraint(&User{}, name); err != nil {
|
||||||
|
t.Fatalf("failed to drop constraint %v, got error %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if DB.Migrator().HasConstraint(&User{}, name) {
|
||||||
|
t.Fatalf("constraint %v should been deleted", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DB.Migrator().CreateConstraint(&User{}, name); err != nil {
|
||||||
|
t.Fatalf("failed to create constraint %v, got error %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !DB.Migrator().HasConstraint(&User{}, name) {
|
||||||
|
t.Fatalf("failed to found constraint %v", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue