GORM Playground Link
https://github.com/go-gorm/playground/pull/648
Description
When gorm parses the table names from the model structs to the name in the db unexpected behaviour can occur:
It appears that gorm has an internal list of abbreviations that will be used in table name parsing (for example: IP, UI, HTTP, etc.). Now if we have a struct or field containing these abbreviations unintentionally gorm will still parse them. For example the following table:
type FBIPassport struct {
...
}
should result in the table name fbi_passports. This isn't the case as the gorm naming strategy detects the abbreviation ip (FBIPassport) in the string and therefore divides it fb_ipassports.
If we were to change this to something like
type CIAPassport struct {
...
}
it will be correctly parsed to cia_passports as it doesn't contain an abbreviation in the recognized list.
In the reproduction this is demonstrated using the NamingStrategy's function TableName, which parses the table names. The same behaviour also occurs when using structs as shown above, as gorm uses the NamingStrategy to generate the table names.
We pinpointed the issue to the function TableName and more specifically the toDBName function, which replaces the common initialisms from the following list:
https://github.com/go-gorm/gorm/blob/e57e5d8884d801caa4ce0307bcd081f7e889e514/schema/naming.go#L109
Comment From: yaofeng-wang
I think that is the default behaviour. It is stated in https://gorm.io/docs/conventions.html#NamingStrategy
GORM allows users change the default naming conventions by overriding the default NamingStrategy, which is used to build TableName, ColumnName, JoinTableName, RelationshipFKName, CheckerName, IndexName, Check out GORM Config for details
One way to make things work in your case would be to define a custom NameReplacer such as
func main() {
ns := schema.NamingStrategy{
NameReplacer: CustomReplacerImpl{},
}
// Results in https_certificates
fmt.Println(ns.TableName("HTTPSCertificate"))
// Results in fbi_passports
fmt.Println(ns.TableName("FBIPassport"))
// Works as intended, resulting in cia_passports
fmt.Println(ns.TableName("CIAPassport"))
}
type CustomReplacerImpl struct{}
func (r CustomReplacerImpl) Replace(name string) string {
switch name {
case "FBIPassport":
return "FbiPassport"
case "HTTPSCertificate":
return "HttpsCertificate"
default:
return name
}
}