2020-01-31 09:17:02 +03:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
2020-02-21 18:51:38 +03:00
|
|
|
"crypto/sha1"
|
2021-04-22 08:11:19 +03:00
|
|
|
"encoding/hex"
|
2021-08-11 11:20:21 +03:00
|
|
|
"regexp"
|
2020-01-31 09:17:02 +03:00
|
|
|
"strings"
|
2020-02-21 18:51:38 +03:00
|
|
|
"unicode/utf8"
|
2020-01-31 09:17:02 +03:00
|
|
|
|
2020-06-02 04:25:55 +03:00
|
|
|
"github.com/jinzhu/inflection"
|
2020-01-31 09:17:02 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Namer namer interface
|
|
|
|
type Namer interface {
|
2020-02-01 07:46:52 +03:00
|
|
|
TableName(table string) string
|
2021-08-11 11:20:21 +03:00
|
|
|
SchemaName(table string) string
|
2020-02-01 19:03:56 +03:00
|
|
|
ColumnName(table, column string) string
|
2020-09-24 06:32:38 +03:00
|
|
|
JoinTableName(joinTable string) string
|
2020-02-22 06:15:51 +03:00
|
|
|
RelationshipFKName(Relationship) string
|
|
|
|
CheckerName(table, column string) string
|
|
|
|
IndexName(table, column string) string
|
2023-10-30 12:15:49 +03:00
|
|
|
UniqueName(table, column string) string
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 03:16:24 +03:00
|
|
|
// Replacer replacer interface like strings.Replacer
|
|
|
|
type Replacer interface {
|
|
|
|
Replace(name string) string
|
|
|
|
}
|
|
|
|
|
2023-10-30 12:15:49 +03:00
|
|
|
var _ Namer = (*NamingStrategy)(nil)
|
|
|
|
|
2020-01-31 09:17:02 +03:00
|
|
|
// NamingStrategy tables, columns naming strategy
|
|
|
|
type NamingStrategy struct {
|
2023-05-30 05:00:48 +03:00
|
|
|
TablePrefix string
|
|
|
|
SingularTable bool
|
|
|
|
NameReplacer Replacer
|
|
|
|
NoLowerCase bool
|
|
|
|
IdentifierMaxLength int
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TableName convert string to table name
|
|
|
|
func (ns NamingStrategy) TableName(str string) string {
|
|
|
|
if ns.SingularTable {
|
2020-11-23 06:24:07 +03:00
|
|
|
return ns.TablePrefix + ns.toDBName(str)
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
2020-11-23 06:24:07 +03:00
|
|
|
return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
|
|
|
|
2021-08-11 11:20:21 +03:00
|
|
|
// SchemaName generate schema name from table name, don't guarantee it is the reverse value of TableName
|
|
|
|
func (ns NamingStrategy) SchemaName(table string) string {
|
|
|
|
table = strings.TrimPrefix(table, ns.TablePrefix)
|
|
|
|
|
|
|
|
if ns.SingularTable {
|
|
|
|
return ns.toSchemaName(table)
|
|
|
|
}
|
|
|
|
return ns.toSchemaName(inflection.Singular(table))
|
|
|
|
}
|
|
|
|
|
2020-01-31 09:17:02 +03:00
|
|
|
// ColumnName convert string to column name
|
2020-02-21 18:51:38 +03:00
|
|
|
func (ns NamingStrategy) ColumnName(table, column string) string {
|
2020-11-23 06:24:07 +03:00
|
|
|
return ns.toDBName(column)
|
2020-02-21 18:51:38 +03:00
|
|
|
}
|
|
|
|
|
2020-02-22 06:15:51 +03:00
|
|
|
// JoinTableName convert string to join table name
|
|
|
|
func (ns NamingStrategy) JoinTableName(str string) string {
|
2021-02-14 03:16:24 +03:00
|
|
|
if !ns.NoLowerCase && strings.ToLower(str) == str {
|
2020-09-28 05:55:27 +03:00
|
|
|
return ns.TablePrefix + str
|
2020-09-06 05:51:21 +03:00
|
|
|
}
|
|
|
|
|
2020-06-30 02:29:15 +03:00
|
|
|
if ns.SingularTable {
|
2020-11-23 06:24:07 +03:00
|
|
|
return ns.TablePrefix + ns.toDBName(str)
|
2020-06-30 02:29:15 +03:00
|
|
|
}
|
2020-11-23 06:24:07 +03:00
|
|
|
return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
|
2020-02-22 06:15:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// RelationshipFKName generate fk name for relation
|
|
|
|
func (ns NamingStrategy) RelationshipFKName(rel Relationship) string {
|
2021-02-01 05:37:12 +03:00
|
|
|
return ns.formatName("fk", rel.Schema.Table, ns.toDBName(rel.Name))
|
2020-02-22 06:15:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// CheckerName generate checker name
|
|
|
|
func (ns NamingStrategy) CheckerName(table, column string) string {
|
2021-02-01 05:37:12 +03:00
|
|
|
return ns.formatName("chk", table, column)
|
2020-02-22 06:15:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// IndexName generate index name
|
2020-02-21 18:51:38 +03:00
|
|
|
func (ns NamingStrategy) IndexName(table, column string) string {
|
2021-02-01 05:37:12 +03:00
|
|
|
return ns.formatName("idx", table, ns.toDBName(column))
|
|
|
|
}
|
|
|
|
|
2023-10-30 12:15:49 +03:00
|
|
|
// UniqueName generate unique constraint name
|
|
|
|
func (ns NamingStrategy) UniqueName(table, column string) string {
|
|
|
|
return ns.formatName("uni", table, ns.toDBName(column))
|
|
|
|
}
|
|
|
|
|
2021-02-01 05:37:12 +03:00
|
|
|
func (ns NamingStrategy) formatName(prefix, table, name string) string {
|
2022-03-20 04:02:45 +03:00
|
|
|
formattedName := strings.ReplaceAll(strings.Join([]string{
|
2021-06-10 05:21:28 +03:00
|
|
|
prefix, table, name,
|
2022-03-20 04:02:45 +03:00
|
|
|
}, "_"), ".", "_")
|
2020-02-21 18:51:38 +03:00
|
|
|
|
2023-05-30 05:00:48 +03:00
|
|
|
if ns.IdentifierMaxLength == 0 {
|
|
|
|
ns.IdentifierMaxLength = 64
|
|
|
|
}
|
|
|
|
|
|
|
|
if utf8.RuneCountInString(formattedName) > ns.IdentifierMaxLength {
|
2020-02-21 18:51:38 +03:00
|
|
|
h := sha1.New()
|
2021-04-19 16:03:39 +03:00
|
|
|
h.Write([]byte(formattedName))
|
2020-02-21 18:51:38 +03:00
|
|
|
bs := h.Sum(nil)
|
|
|
|
|
2023-05-30 05:00:48 +03:00
|
|
|
formattedName = formattedName[0:ns.IdentifierMaxLength-8] + hex.EncodeToString(bs)[:8]
|
2020-02-21 18:51:38 +03:00
|
|
|
}
|
2021-04-19 16:03:39 +03:00
|
|
|
return formattedName
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// https://github.com/golang/lint/blob/master/lint.go#L770
|
|
|
|
commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UID", "UI", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
|
|
|
|
commonInitialismsReplacer *strings.Replacer
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
2021-03-04 14:44:15 +03:00
|
|
|
commonInitialismsForReplacer := make([]string, 0, len(commonInitialisms))
|
2020-01-31 09:17:02 +03:00
|
|
|
for _, initialism := range commonInitialisms {
|
|
|
|
commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
|
|
|
|
}
|
|
|
|
commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
|
|
|
|
}
|
|
|
|
|
2020-11-23 06:24:07 +03:00
|
|
|
func (ns NamingStrategy) toDBName(name string) string {
|
2020-01-31 09:17:02 +03:00
|
|
|
if name == "" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-11-23 06:24:07 +03:00
|
|
|
if ns.NameReplacer != nil {
|
2022-02-08 12:06:10 +03:00
|
|
|
tmpName := ns.NameReplacer.Replace(name)
|
|
|
|
|
|
|
|
if tmpName == "" {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
name = tmpName
|
2020-11-23 06:24:07 +03:00
|
|
|
}
|
|
|
|
|
2021-02-14 03:16:24 +03:00
|
|
|
if ns.NoLowerCase {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2020-01-31 09:17:02 +03:00
|
|
|
var (
|
|
|
|
value = commonInitialismsReplacer.Replace(name)
|
|
|
|
buf strings.Builder
|
|
|
|
lastCase, nextCase, nextNumber bool // upper case == true
|
|
|
|
curCase = value[0] <= 'Z' && value[0] >= 'A'
|
|
|
|
)
|
|
|
|
|
|
|
|
for i, v := range value[:len(value)-1] {
|
|
|
|
nextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'
|
|
|
|
nextNumber = value[i+1] >= '0' && value[i+1] <= '9'
|
|
|
|
|
|
|
|
if curCase {
|
|
|
|
if lastCase && (nextCase || nextNumber) {
|
|
|
|
buf.WriteRune(v + 32)
|
|
|
|
} else {
|
|
|
|
if i > 0 && value[i-1] != '_' && value[i+1] != '_' {
|
|
|
|
buf.WriteByte('_')
|
|
|
|
}
|
|
|
|
buf.WriteRune(v + 32)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buf.WriteRune(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
lastCase = curCase
|
|
|
|
curCase = nextCase
|
|
|
|
}
|
|
|
|
|
|
|
|
if curCase {
|
|
|
|
if !lastCase && len(value) > 1 {
|
|
|
|
buf.WriteByte('_')
|
|
|
|
}
|
|
|
|
buf.WriteByte(value[len(value)-1] + 32)
|
|
|
|
} else {
|
|
|
|
buf.WriteByte(value[len(value)-1])
|
|
|
|
}
|
2020-11-16 10:16:15 +03:00
|
|
|
ret := buf.String()
|
|
|
|
return ret
|
2020-01-31 09:17:02 +03:00
|
|
|
}
|
2021-08-11 11:20:21 +03:00
|
|
|
|
|
|
|
func (ns NamingStrategy) toSchemaName(name string) string {
|
2022-02-20 14:55:04 +03:00
|
|
|
result := strings.ReplaceAll(strings.Title(strings.ReplaceAll(name, "_", " ")), " ", "")
|
2021-08-11 11:20:21 +03:00
|
|
|
for _, initialism := range commonInitialisms {
|
|
|
|
result = regexp.MustCompile(strings.Title(strings.ToLower(initialism))+"([A-Z]|$|_)").ReplaceAllString(result, initialism+"$1")
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|