Add RemoveForeignKey functionality.

Works for mssql, mysql, postgresql. Droping foreign keys for sqlite isn't supported without a work-around.
This commit is contained in:
Samuel Gavinio 2017-05-07 22:28:21 +08:00
parent 9acaa33324
commit 7b743bdd2b
6 changed files with 58 additions and 1 deletions

View File

@ -29,6 +29,8 @@ type Dialect interface {
HasForeignKey(tableName string, foreignKeyName string) bool HasForeignKey(tableName string, foreignKeyName string) bool
// RemoveIndex remove index // RemoveIndex remove index
RemoveIndex(tableName string, indexName string) error RemoveIndex(tableName string, indexName string) error
// RemoveForeignKey remove foreign key
RemoveForeignKey(tableName string, foreignKeyName string) error
// HasTable check has table or not // HasTable check has table or not
HasTable(tableName string) bool HasTable(tableName string) bool
// HasColumn check has column or not // HasColumn check has column or not

View File

@ -105,6 +105,11 @@ func (s commonDialect) HasForeignKey(tableName string, foreignKeyName string) bo
return false return false
} }
func (s commonDialect) RemoveForeignKey(tableName string, foreignKeyName string) error {
_, err := s.db.Exec(fmt.Sprintf("ALTER TABLE %v DROP CONSTRAINT %v", tableName, foreignKeyName))
return err
}
func (s commonDialect) HasTable(tableName string) bool { func (s commonDialect) HasTable(tableName string) bool {
var count int var count int
s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", s.CurrentDatabase(), tableName).Scan(&count) s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", s.CurrentDatabase(), tableName).Scan(&count)

View File

@ -132,6 +132,11 @@ func (s mysql) HasForeignKey(tableName string, foreignKeyName string) bool {
return count > 0 return count > 0
} }
func (s mysql) RemoveForeignKey(tableName string, foreignKeyName string) error {
_, err := s.db.Exec(fmt.Sprintf("ALTER TABLE %v DROP FOREIGN KEY %v", s.Quote(tableName), foreignKeyName))
return err
}
func (s mysql) CurrentDatabase() (name string) { func (s mysql) CurrentDatabase() (name string) {
s.db.QueryRow("SELECT DATABASE()").Scan(&name) s.db.QueryRow("SELECT DATABASE()").Scan(&name)
return return

View File

@ -602,6 +602,14 @@ func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate
return scope.db return scope.db
} }
// RemoveForeignKey Removes foreign key from the given scope, e.g:
// db.Model(&User{}).RemoveForeignKey("user_city_id_city_id_foreign")
func (s *DB) RemoveForeignKey(keyName string) *DB {
scope := s.NewScope(s.Value)
scope.removeForeignKey(keyName)
return scope.db
}
// Association start `Association Mode` to handler relations things easir in that mode, refer: https://jinzhu.github.io/gorm/associations.html#association-mode // Association start `Association Mode` to handler relations things easir in that mode, refer: https://jinzhu.github.io/gorm/associations.html#association-mode
func (s *DB) Association(column string) *Association { func (s *DB) Association(column string) *Association {
var err error var err error

View File

@ -191,6 +191,16 @@ type Comment struct {
Post Post Post Post
} }
type Class struct {
Id int64
Year string
}
type Student struct {
Id int64
ClassID int64
}
// Scanner // Scanner
type NullValue struct { type NullValue struct {
Id int64 Id int64
@ -254,7 +264,7 @@ func runMigration() {
DB.Exec(fmt.Sprintf("drop table %v;", table)) DB.Exec(fmt.Sprintf("drop table %v;", table))
} }
values := []interface{}{&Short{}, &ReallyLongThingThatReferencesShort{}, &ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Hamster{}, &Toy{}, &ElementWithIgnoredField{}} values := []interface{}{&Short{}, &ReallyLongThingThatReferencesShort{}, &ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Hamster{}, &Toy{}, &ElementWithIgnoredField{}, &Class{}, &Student{}}
for _, value := range values { for _, value := range values {
DB.DropTable(value) DB.DropTable(value)
} }
@ -332,6 +342,25 @@ func TestIndexes(t *testing.T) {
} }
} }
func TestForeignKeys(t *testing.T) {
if err := DB.Model(&Student{}).AddForeignKey("class_id", "classes (id)", "RESTRICT", "RESTRICT").Error; err != nil {
t.Errorf("Got error while trying to create foreign key: %+v", err)
}
scope := DB.NewScope(&Student{})
if !scope.Dialect().HasForeignKey(scope.TableName(), "student_class_id_class_id_foreign") {
t.Errorf("Student should have foreign key students_class_id_classes_id_foreign")
}
if err := DB.Model(&Student{}).RemoveForeignKey("students_class_id_classes_id_foreign").Error; err != nil {
t.Errorf("Got error while trying to remove foreign key: %+v", err)
}
if scope.Dialect().HasForeignKey(scope.TableName(), "student_class_id_class_id_foreign") {
t.Errorf("Student should no longer have foreign key students_class_id_classes_id_foreign")
}
}
type EmailWithIdx struct { type EmailWithIdx struct {
Id int64 Id int64
UserId int64 UserId int64

View File

@ -1168,6 +1168,14 @@ func (scope *Scope) removeIndex(indexName string) {
scope.Dialect().RemoveIndex(scope.TableName(), indexName) scope.Dialect().RemoveIndex(scope.TableName(), indexName)
} }
func (scope *Scope) removeForeignKey(keyName string) {
if !scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
return
}
scope.Dialect().RemoveForeignKey(scope.TableName(), keyName)
}
func (scope *Scope) autoMigrate() *Scope { func (scope *Scope) autoMigrate() *Scope {
tableName := scope.TableName() tableName := scope.TableName()
quotedTableName := scope.QuotedTableName() quotedTableName := scope.QuotedTableName()