diff --git a/gorm.go b/gorm.go index 9a70c3d2..f0e0b7bf 100644 --- a/gorm.go +++ b/gorm.go @@ -144,7 +144,20 @@ func Open(dialector Dialector, opts ...Option) (db *DB, err error) { } if config.NamingStrategy == nil { - config.NamingStrategy = schema.NamingStrategy{} + // Set default value of IdentifierMaxLength according to the database type + identifierMaxLength := 64 + switch dialector.Name() { + case "mysql": + identifierMaxLength = 64 + case "postgres": + identifierMaxLength = 63 + case "sqlite": + identifierMaxLength = 64 + case "sqlserver": + identifierMaxLength = 128 + } + + config.NamingStrategy = schema.NamingStrategy{NamingStrategyConfig: schema.NamingStrategyConfig{IdentifierMaxLength: identifierMaxLength}} } if config.Logger == nil { diff --git a/schema/check_test.go b/schema/check_test.go index eda043b7..a327d882 100644 --- a/schema/check_test.go +++ b/schema/check_test.go @@ -15,7 +15,7 @@ type UserCheck struct { } func TestParseCheck(t *testing.T) { - user, err := schema.Parse(&UserCheck{}, &sync.Map{}, schema.NamingStrategy{}) + user, err := schema.Parse(&UserCheck{}, &sync.Map{}, schema.NamingStrategy{NamingStrategyConfig: schema.NamingStrategyConfig{IdentifierMaxLength: 64}}) if err != nil { t.Fatalf("failed to parse user check, got error %v", err) } diff --git a/schema/index_test.go b/schema/index_test.go index 890327de..af8613a8 100644 --- a/schema/index_test.go +++ b/schema/index_test.go @@ -52,7 +52,7 @@ type CompIdxLevel2B struct { } func TestParseIndex(t *testing.T) { - user, err := schema.Parse(&UserIndex{}, &sync.Map{}, schema.NamingStrategy{}) + user, err := schema.Parse(&UserIndex{}, &sync.Map{}, schema.NamingStrategy{NamingStrategyConfig: schema.NamingStrategyConfig{IdentifierMaxLength: 64}}) if err != nil { t.Fatalf("failed to parse user index, got error %v", err) } diff --git a/schema/naming.go b/schema/naming.go index a258beed..228063ba 100644 --- a/schema/naming.go +++ b/schema/naming.go @@ -32,6 +32,17 @@ type NamingStrategy struct { SingularTable bool NameReplacer Replacer NoLowerCase bool + NamingStrategyConfig +} + +// This struct is used to configure the behavior NamingStrategy, according to DB. +// For example, in MySQL, the maximum length of an identifier is 64 characters. +// In PostgreSQL, the maximum length of an identifier is 63 characters. +// In SQL Server, the maximum length of an identifier is 128 characters. +// In SQLite, the maximum length of an identifier is unlimited. +// In future, we may add more options to NamingStrategyConfig. +type NamingStrategyConfig struct { + IdentifierMaxLength int } // TableName convert string to table name @@ -89,12 +100,12 @@ func (ns NamingStrategy) formatName(prefix, table, name string) string { prefix, table, name, }, "_"), ".", "_") - if utf8.RuneCountInString(formattedName) > 64 { + if utf8.RuneCountInString(formattedName) > ns.IdentifierMaxLength { h := sha1.New() h.Write([]byte(formattedName)) bs := h.Sum(nil) - formattedName = formattedName[0:56] + hex.EncodeToString(bs)[:8] + formattedName = formattedName[0:ns.IdentifierMaxLength-8] + hex.EncodeToString(bs)[:8] } return formattedName } diff --git a/schema/naming_test.go b/schema/naming_test.go index 3f598c33..14ca384b 100644 --- a/schema/naming_test.go +++ b/schema/naming_test.go @@ -57,9 +57,10 @@ func TestToDBName(t *testing.T) { func TestNamingStrategy(t *testing.T) { ns := NamingStrategy{ - TablePrefix: "public.", - SingularTable: true, - NameReplacer: strings.NewReplacer("CID", "Cid"), + TablePrefix: "public.", + SingularTable: true, + NameReplacer: strings.NewReplacer("CID", "Cid"), + NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 64}, } idxName := ns.IndexName("public.table", "name") @@ -111,7 +112,8 @@ func TestCustomReplacer(t *testing.T) { return strings.NewReplacer("CID", "_Cid").Replace(replaced) }, }, - NoLowerCase: false, + NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 64}, + NoLowerCase: false, } idxName := ns.IndexName("public.table", "name") @@ -155,7 +157,8 @@ func TestCustomReplacerWithNoLowerCase(t *testing.T) { return strings.NewReplacer("CID", "_Cid").Replace(replaced) }, }, - NoLowerCase: true, + NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 64}, + NoLowerCase: true, } idxName := ns.IndexName("public.table", "name") @@ -189,13 +192,27 @@ func TestCustomReplacerWithNoLowerCase(t *testing.T) { } } -func TestFormatNameWithStringLongerThan64Characters(t *testing.T) { - ns := NamingStrategy{} +func TestFormatNameWithStringLongerThanMaxIdentifierLength(t *testing.T) { + ns := NamingStrategy{NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 63}} formattedName := ns.formatName("prefix", "table", "thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString") + if formattedName != "prefix_table_thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVer180f2c67" { + t.Errorf("invalid formatted name generated, got %v", formattedName) + } + + ns = NamingStrategy{NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 64}} + + formattedName = ns.formatName("prefix", "table", "thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString") if formattedName != "prefix_table_thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVery180f2c67" { t.Errorf("invalid formatted name generated, got %v", formattedName) } + + ns = NamingStrategy{NamingStrategyConfig: NamingStrategyConfig{IdentifierMaxLength: 128}} + + formattedName = ns.formatName("prefix", "table", "thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString") + if formattedName != "prefix_table_thisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVery81f06061" { + t.Errorf("invalid formatted name generated, got %v", formattedName) + } } func TestReplaceEmptyTableName(t *testing.T) { diff --git a/schema/relationship_test.go b/schema/relationship_test.go index 85c45589..4abde0d9 100644 --- a/schema/relationship_test.go +++ b/schema/relationship_test.go @@ -642,7 +642,7 @@ func TestParseConstraintNameWithSchemaQualifiedLongTableName(t *testing.T) { s, err := schema.Parse( &Book{}, &sync.Map{}, - schema.NamingStrategy{}, + schema.NamingStrategy{NamingStrategyConfig: schema.NamingStrategyConfig{IdentifierMaxLength: 64}}, ) if err != nil { t.Fatalf("Failed to parse schema")