mirror of
https://github.com/zhufuyi/sponge.git
synced 2025-09-26 20:51:14 +08:00
✨Fix cyclomatic complexity 25 of func AdaptiveMysqlDsn
is high (> 20) (gocyclo)
This commit is contained in:
200
pkg/utils/dsn.go
200
pkg/utils/dsn.go
@@ -11,107 +11,119 @@ func AdaptiveMysqlDsn(dsn string) string {
|
||||
// remove optional scheme prefix
|
||||
dsn = strings.ReplaceAll(dsn, "mysql://", "")
|
||||
|
||||
// ensure a valid network/address section for go-sql-driver/mysql
|
||||
// Expected forms:
|
||||
// user:pass@tcp(127.0.0.1:3306)/db
|
||||
// user:pass@unix(/path/mysql.sock)/db
|
||||
// If it's like '@(127.0.0.1:3306)' → add 'tcp'
|
||||
// If it's like '@127.0.0.1:3306' → wrap to '@tcp(127.0.0.1:3306)'
|
||||
at := strings.Index(dsn, "@")
|
||||
if at != -1 {
|
||||
afterAt := dsn[at+1:]
|
||||
slashIdx := strings.Index(afterAt, "/")
|
||||
if slashIdx != -1 {
|
||||
addrPart := afterAt[:slashIdx]
|
||||
// If empty addrPart, nothing to fix
|
||||
if addrPart != "" {
|
||||
if strings.HasPrefix(addrPart, "(") {
|
||||
// missing protocol
|
||||
dsn = strings.Replace(dsn, "@(", "@tcp(", 1)
|
||||
} else if !(strings.HasPrefix(addrPart, "tcp(") || strings.HasPrefix(addrPart, "unix(")) {
|
||||
// no parentheses and no protocol → wrap with tcp()
|
||||
dsn = strings.Replace(dsn, "@"+addrPart, "@tcp("+addrPart+")", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dsn = ensureNetworkAddress(dsn)
|
||||
return ensureCharsetAndCollation(dsn)
|
||||
}
|
||||
|
||||
// ensure the connection prefers utf8mb4 to avoid collation mismatch
|
||||
// issues with MySQL 8 (e.g. mixing utf8mb3_general_ci and utf8mb4_0900_ai_ci).
|
||||
qIdx := strings.Index(dsn, "?")
|
||||
if qIdx == -1 {
|
||||
// no query string → add charset parameter
|
||||
return dsn + "?charset=utf8mb4"
|
||||
}
|
||||
// helper: ensure network/address section is valid for go-sql-driver/mysql
|
||||
func ensureNetworkAddress(dsn string) string {
|
||||
at := strings.Index(dsn, "@")
|
||||
if at == -1 {
|
||||
return dsn
|
||||
}
|
||||
|
||||
prefix := dsn[:qIdx]
|
||||
queryStr := dsn[qIdx+1:]
|
||||
parts := strings.Split(queryStr, "&")
|
||||
afterAt := dsn[at+1:]
|
||||
slashIdx := strings.Index(afterAt, "/")
|
||||
if slashIdx == -1 {
|
||||
return dsn
|
||||
}
|
||||
|
||||
hasCharset := false
|
||||
hasCollation := false
|
||||
for i, p := range parts {
|
||||
if strings.HasPrefix(p, "charset=") {
|
||||
hasCharset = true
|
||||
val := strings.TrimPrefix(p, "charset=")
|
||||
// split by comma and de-duplicate while ensuring utf8mb4 comes first if present/added
|
||||
charsets := []string{}
|
||||
for _, cs := range strings.Split(val, ",") {
|
||||
cs = strings.TrimSpace(cs)
|
||||
if cs == "" {
|
||||
continue
|
||||
}
|
||||
// skip duplicates
|
||||
dup := false
|
||||
for _, existing := range charsets {
|
||||
if strings.EqualFold(existing, cs) {
|
||||
dup = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !dup {
|
||||
charsets = append(charsets, cs)
|
||||
}
|
||||
}
|
||||
addrPart := afterAt[:slashIdx]
|
||||
if addrPart == "" {
|
||||
return dsn
|
||||
}
|
||||
|
||||
// ensure utf8mb4 is present and at the first position
|
||||
containsUtf8mb4 := false
|
||||
for _, cs := range charsets {
|
||||
if strings.EqualFold(cs, "utf8mb4") {
|
||||
containsUtf8mb4 = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !containsUtf8mb4 {
|
||||
charsets = append([]string{"utf8mb4"}, charsets...)
|
||||
} else if len(charsets) > 0 && !strings.EqualFold(charsets[0], "utf8mb4") {
|
||||
// move utf8mb4 to front
|
||||
newOrder := []string{"utf8mb4"}
|
||||
for _, cs := range charsets {
|
||||
if !strings.EqualFold(cs, "utf8mb4") {
|
||||
newOrder = append(newOrder, cs)
|
||||
}
|
||||
}
|
||||
charsets = newOrder
|
||||
}
|
||||
if strings.HasPrefix(addrPart, "(") {
|
||||
// missing protocol, add tcp
|
||||
return strings.Replace(dsn, "@(", "@tcp(", 1)
|
||||
}
|
||||
|
||||
parts[i] = "charset=" + strings.Join(charsets, ",")
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(p, "collation=") {
|
||||
hasCollation = true
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(addrPart, "tcp(") || strings.HasPrefix(addrPart, "unix(") {
|
||||
return dsn
|
||||
}
|
||||
|
||||
if !hasCharset {
|
||||
parts = append(parts, "charset=utf8mb4")
|
||||
}
|
||||
if !hasCollation {
|
||||
// default to a broadly compatible utf8mb4 collation
|
||||
parts = append(parts, "collation=utf8mb4_general_ci")
|
||||
}
|
||||
// no parentheses and no protocol → wrap with tcp()
|
||||
return strings.Replace(dsn, "@"+addrPart, "@tcp("+addrPart+")", 1)
|
||||
}
|
||||
|
||||
return prefix + "?" + strings.Join(parts, "&")
|
||||
// helper: ensure charset utf8mb4 and a reasonable collation are present
|
||||
func ensureCharsetAndCollation(dsn string) string {
|
||||
qIdx := strings.Index(dsn, "?")
|
||||
if qIdx == -1 {
|
||||
return dsn + "?charset=utf8mb4"
|
||||
}
|
||||
|
||||
prefix := dsn[:qIdx]
|
||||
queryStr := dsn[qIdx+1:]
|
||||
parts := strings.Split(queryStr, "&")
|
||||
|
||||
hasCharset := false
|
||||
hasCollation := false
|
||||
for i, p := range parts {
|
||||
if strings.HasPrefix(p, "charset=") {
|
||||
hasCharset = true
|
||||
parts[i] = "charset=" + normalizeCharsets(strings.TrimPrefix(p, "charset="))
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(p, "collation=") {
|
||||
hasCollation = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasCharset {
|
||||
parts = append(parts, "charset=utf8mb4")
|
||||
}
|
||||
if !hasCollation {
|
||||
parts = append(parts, "collation=utf8mb4_general_ci")
|
||||
}
|
||||
|
||||
return prefix + "?" + strings.Join(parts, "&")
|
||||
}
|
||||
|
||||
// normalizeCharsets deduplicates a comma-separated charset list and ensures utf8mb4 is first
|
||||
func normalizeCharsets(val string) string {
|
||||
pieces := strings.Split(val, ",")
|
||||
seen := map[string]bool{}
|
||||
ordered := []string{}
|
||||
for _, cs := range pieces {
|
||||
cs = strings.TrimSpace(cs)
|
||||
if cs == "" {
|
||||
continue
|
||||
}
|
||||
lower := strings.ToLower(cs)
|
||||
if seen[lower] {
|
||||
continue
|
||||
}
|
||||
seen[lower] = true
|
||||
ordered = append(ordered, cs)
|
||||
}
|
||||
|
||||
// ensure utf8mb4 is present and at the front (case-insensitive)
|
||||
found := -1
|
||||
for i, cs := range ordered {
|
||||
if strings.EqualFold(cs, "utf8mb4") {
|
||||
found = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if found == -1 {
|
||||
ordered = append([]string{"utf8mb4"}, ordered...)
|
||||
} else if found != 0 {
|
||||
// move to front
|
||||
front := []string{"utf8mb4"}
|
||||
for i, cs := range ordered {
|
||||
if i == found {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(cs, "utf8mb4") {
|
||||
continue
|
||||
}
|
||||
front = append(front, cs)
|
||||
}
|
||||
ordered = front
|
||||
}
|
||||
|
||||
return strings.Join(ordered, ",")
|
||||
}
|
||||
|
||||
// AdaptivePostgresqlDsn convert postgres dsn to kv string
|
||||
|
Reference in New Issue
Block a user