forked from mirror/gorm
Merge pull request #1029 from nkovacs/mysql-foreign-key-length-fix
Mysql foreign key length fix
This commit is contained in:
commit
4c9c024939
|
@ -2,6 +2,7 @@ package gorm_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -840,3 +841,26 @@ func TestForeignKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLongForeignKey(t *testing.T) {
|
||||||
|
if dialect := os.Getenv("GORM_DIALECT"); dialect == "" || dialect == "sqlite" {
|
||||||
|
// sqlite does not support ADD CONSTRAINT in ALTER TABLE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetScope := DB.NewScope(&ReallyLongTableNameToTestMySQLNameLengthLimit{})
|
||||||
|
targetTableName := targetScope.TableName()
|
||||||
|
modelScope := DB.NewScope(&NotSoLongTableName{})
|
||||||
|
modelField, ok := modelScope.FieldByName("ReallyLongThingID")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Failed to get field by name: ReallyLongThingID")
|
||||||
|
}
|
||||||
|
targetField, ok := targetScope.FieldByName("ID")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Failed to get field by name: ID")
|
||||||
|
}
|
||||||
|
dest := fmt.Sprintf("%v(%v)", targetTableName, targetField.DBName)
|
||||||
|
err := DB.Model(&NotSoLongTableName{}).AddForeignKey(modelField.DBName, dest, "CASCADE", "CASCADE").Error
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(fmt.Sprintf("Failed to create foreign key: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,9 @@ type Dialect interface {
|
||||||
SelectFromDummyTable() string
|
SelectFromDummyTable() string
|
||||||
// LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING`
|
// LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING`
|
||||||
LastInsertIDReturningSuffix(tableName, columnName string) string
|
LastInsertIDReturningSuffix(tableName, columnName string) string
|
||||||
|
|
||||||
|
// BuildForeignKeyName returns a foreign key name for the given table, field and reference
|
||||||
|
BuildForeignKeyName(tableName, field, dest string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialectsMap = map[string]Dialect{}
|
var dialectsMap = map[string]Dialect{}
|
||||||
|
|
|
@ -4,12 +4,18 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultForeignKeyNamer contains the default foreign key name generator method
|
||||||
|
type DefaultForeignKeyNamer struct {
|
||||||
|
}
|
||||||
|
|
||||||
type commonDialect struct {
|
type commonDialect struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
DefaultForeignKeyNamer
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -135,3 +141,9 @@ func (commonDialect) SelectFromDummyTable() string {
|
||||||
func (commonDialect) LastInsertIDReturningSuffix(tableName, columnName string) string {
|
func (commonDialect) LastInsertIDReturningSuffix(tableName, columnName string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (DefaultForeignKeyNamer) BuildForeignKeyName(tableName, field, dest string) string {
|
||||||
|
keyName := fmt.Sprintf("%s_%s_%s_foreign", tableName, field, dest)
|
||||||
|
keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(keyName, "_")
|
||||||
|
return keyName
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package gorm
|
package gorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -115,3 +117,18 @@ func (s mysql) currentDatabase() (name string) {
|
||||||
func (mysql) SelectFromDummyTable() string {
|
func (mysql) SelectFromDummyTable() string {
|
||||||
return "FROM DUAL"
|
return "FROM DUAL"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s mysql) BuildForeignKeyName(tableName, field, dest string) string {
|
||||||
|
keyName := s.commonDialect.BuildForeignKeyName(tableName, field, dest)
|
||||||
|
if len(keyName) <= 64 {
|
||||||
|
return keyName
|
||||||
|
}
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write([]byte(keyName))
|
||||||
|
bs := h.Sum(nil)
|
||||||
|
|
||||||
|
// sha1 is 40 digits, keep first 24 characters of destination
|
||||||
|
keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(dest, "_")
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%x", keyName[:24], bs)
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ func init() {
|
||||||
|
|
||||||
type mssql struct {
|
type mssql struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
gorm.DefaultForeignKeyNamer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mssql) GetName() string {
|
func (mssql) GetName() string {
|
||||||
|
|
|
@ -39,6 +39,16 @@ type User struct {
|
||||||
IgnoredPointer *User `sql:"-"`
|
IgnoredPointer *User `sql:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NotSoLongTableName struct {
|
||||||
|
Id int64
|
||||||
|
ReallyLongThingID int64
|
||||||
|
ReallyLongThing ReallyLongTableNameToTestMySQLNameLengthLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReallyLongTableNameToTestMySQLNameLengthLimit struct {
|
||||||
|
Id int64
|
||||||
|
}
|
||||||
|
|
||||||
type CreditCard struct {
|
type CreditCard struct {
|
||||||
ID int8
|
ID int8
|
||||||
Number string
|
Number string
|
||||||
|
@ -231,7 +241,7 @@ func runMigration() {
|
||||||
DB.Exec(fmt.Sprintf("drop table %v;", table))
|
DB.Exec(fmt.Sprintf("drop table %v;", table))
|
||||||
}
|
}
|
||||||
|
|
||||||
values := []interface{}{&Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Toy{}}
|
values := []interface{}{&ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Toy{}}
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
DB.DropTable(value)
|
DB.DropTable(value)
|
||||||
}
|
}
|
||||||
|
|
3
scope.go
3
scope.go
|
@ -1117,8 +1117,7 @@ func (scope *Scope) addIndex(unique bool, indexName string, column ...string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scope *Scope) addForeignKey(field string, dest string, onDelete string, onUpdate string) {
|
func (scope *Scope) addForeignKey(field string, dest string, onDelete string, onUpdate string) {
|
||||||
var keyName = fmt.Sprintf("%s_%s_%s_foreign", scope.TableName(), field, dest)
|
keyName := scope.Dialect().BuildForeignKeyName(scope.TableName(), field, dest)
|
||||||
keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(keyName, "_")
|
|
||||||
|
|
||||||
if scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
|
if scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue