Skip to content

Commit 4b8ed64

Browse files
committed
✨Fix cyclomatic complexity 25 of func AdaptiveMysqlDsn is high (> 20) (gocyclo)
1 parent cc9cf38 commit 4b8ed64

File tree

1 file changed

+110
-98
lines changed

1 file changed

+110
-98
lines changed

pkg/utils/dsn.go

Lines changed: 110 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -11,107 +11,119 @@ func AdaptiveMysqlDsn(dsn string) string {
1111
// remove optional scheme prefix
1212
dsn = strings.ReplaceAll(dsn, "mysql://", "")
1313

14-
// ensure a valid network/address section for go-sql-driver/mysql
15-
// Expected forms:
16-
// user:pass@tcp(127.0.0.1:3306)/db
17-
// user:pass@unix(/path/mysql.sock)/db
18-
// If it's like '@(127.0.0.1:3306)' → add 'tcp'
19-
// If it's like '@127.0.0.1:3306' → wrap to '@tcp(127.0.0.1:3306)'
20-
at := strings.Index(dsn, "@")
21-
if at != -1 {
22-
afterAt := dsn[at+1:]
23-
slashIdx := strings.Index(afterAt, "/")
24-
if slashIdx != -1 {
25-
addrPart := afterAt[:slashIdx]
26-
// If empty addrPart, nothing to fix
27-
if addrPart != "" {
28-
if strings.HasPrefix(addrPart, "(") {
29-
// missing protocol
30-
dsn = strings.Replace(dsn, "@(", "@tcp(", 1)
31-
} else if !(strings.HasPrefix(addrPart, "tcp(") || strings.HasPrefix(addrPart, "unix(")) {
32-
// no parentheses and no protocol → wrap with tcp()
33-
dsn = strings.Replace(dsn, "@"+addrPart, "@tcp("+addrPart+")", 1)
34-
}
35-
}
36-
}
37-
}
38-
39-
// ensure the connection prefers utf8mb4 to avoid collation mismatch
40-
// issues with MySQL 8 (e.g. mixing utf8mb3_general_ci and utf8mb4_0900_ai_ci).
41-
qIdx := strings.Index(dsn, "?")
42-
if qIdx == -1 {
43-
// no query string → add charset parameter
44-
return dsn + "?charset=utf8mb4"
45-
}
14+
dsn = ensureNetworkAddress(dsn)
15+
return ensureCharsetAndCollation(dsn)
16+
}
4617

47-
prefix := dsn[:qIdx]
48-
queryStr := dsn[qIdx+1:]
49-
parts := strings.Split(queryStr, "&")
50-
51-
hasCharset := false
52-
hasCollation := false
53-
for i, p := range parts {
54-
if strings.HasPrefix(p, "charset=") {
55-
hasCharset = true
56-
val := strings.TrimPrefix(p, "charset=")
57-
// split by comma and de-duplicate while ensuring utf8mb4 comes first if present/added
58-
charsets := []string{}
59-
for _, cs := range strings.Split(val, ",") {
60-
cs = strings.TrimSpace(cs)
61-
if cs == "" {
62-
continue
63-
}
64-
// skip duplicates
65-
dup := false
66-
for _, existing := range charsets {
67-
if strings.EqualFold(existing, cs) {
68-
dup = true
69-
break
70-
}
71-
}
72-
if !dup {
73-
charsets = append(charsets, cs)
74-
}
75-
}
76-
77-
// ensure utf8mb4 is present and at the first position
78-
containsUtf8mb4 := false
79-
for _, cs := range charsets {
80-
if strings.EqualFold(cs, "utf8mb4") {
81-
containsUtf8mb4 = true
82-
break
83-
}
84-
}
85-
if !containsUtf8mb4 {
86-
charsets = append([]string{"utf8mb4"}, charsets...)
87-
} else if len(charsets) > 0 && !strings.EqualFold(charsets[0], "utf8mb4") {
88-
// move utf8mb4 to front
89-
newOrder := []string{"utf8mb4"}
90-
for _, cs := range charsets {
91-
if !strings.EqualFold(cs, "utf8mb4") {
92-
newOrder = append(newOrder, cs)
93-
}
94-
}
95-
charsets = newOrder
96-
}
97-
98-
parts[i] = "charset=" + strings.Join(charsets, ",")
99-
break
100-
}
101-
if strings.HasPrefix(p, "collation=") {
102-
hasCollation = true
103-
}
104-
}
18+
// helper: ensure network/address section is valid for go-sql-driver/mysql
19+
func ensureNetworkAddress(dsn string) string {
20+
at := strings.Index(dsn, "@")
21+
if at == -1 {
22+
return dsn
23+
}
24+
25+
afterAt := dsn[at+1:]
26+
slashIdx := strings.Index(afterAt, "/")
27+
if slashIdx == -1 {
28+
return dsn
29+
}
30+
31+
addrPart := afterAt[:slashIdx]
32+
if addrPart == "" {
33+
return dsn
34+
}
35+
36+
if strings.HasPrefix(addrPart, "(") {
37+
// missing protocol, add tcp
38+
return strings.Replace(dsn, "@(", "@tcp(", 1)
39+
}
40+
41+
if strings.HasPrefix(addrPart, "tcp(") || strings.HasPrefix(addrPart, "unix(") {
42+
return dsn
43+
}
44+
45+
// no parentheses and no protocol → wrap with tcp()
46+
return strings.Replace(dsn, "@"+addrPart, "@tcp("+addrPart+")", 1)
47+
}
10548

106-
if !hasCharset {
107-
parts = append(parts, "charset=utf8mb4")
108-
}
109-
if !hasCollation {
110-
// default to a broadly compatible utf8mb4 collation
111-
parts = append(parts, "collation=utf8mb4_general_ci")
112-
}
49+
// helper: ensure charset utf8mb4 and a reasonable collation are present
50+
func ensureCharsetAndCollation(dsn string) string {
51+
qIdx := strings.Index(dsn, "?")
52+
if qIdx == -1 {
53+
return dsn + "?charset=utf8mb4"
54+
}
55+
56+
prefix := dsn[:qIdx]
57+
queryStr := dsn[qIdx+1:]
58+
parts := strings.Split(queryStr, "&")
59+
60+
hasCharset := false
61+
hasCollation := false
62+
for i, p := range parts {
63+
if strings.HasPrefix(p, "charset=") {
64+
hasCharset = true
65+
parts[i] = "charset=" + normalizeCharsets(strings.TrimPrefix(p, "charset="))
66+
break
67+
}
68+
if strings.HasPrefix(p, "collation=") {
69+
hasCollation = true
70+
}
71+
}
72+
73+
if !hasCharset {
74+
parts = append(parts, "charset=utf8mb4")
75+
}
76+
if !hasCollation {
77+
parts = append(parts, "collation=utf8mb4_general_ci")
78+
}
79+
80+
return prefix + "?" + strings.Join(parts, "&")
81+
}
11382

114-
return prefix + "?" + strings.Join(parts, "&")
83+
// normalizeCharsets deduplicates a comma-separated charset list and ensures utf8mb4 is first
84+
func normalizeCharsets(val string) string {
85+
pieces := strings.Split(val, ",")
86+
seen := map[string]bool{}
87+
ordered := []string{}
88+
for _, cs := range pieces {
89+
cs = strings.TrimSpace(cs)
90+
if cs == "" {
91+
continue
92+
}
93+
lower := strings.ToLower(cs)
94+
if seen[lower] {
95+
continue
96+
}
97+
seen[lower] = true
98+
ordered = append(ordered, cs)
99+
}
100+
101+
// ensure utf8mb4 is present and at the front (case-insensitive)
102+
found := -1
103+
for i, cs := range ordered {
104+
if strings.EqualFold(cs, "utf8mb4") {
105+
found = i
106+
break
107+
}
108+
}
109+
if found == -1 {
110+
ordered = append([]string{"utf8mb4"}, ordered...)
111+
} else if found != 0 {
112+
// move to front
113+
front := []string{"utf8mb4"}
114+
for i, cs := range ordered {
115+
if i == found {
116+
continue
117+
}
118+
if strings.EqualFold(cs, "utf8mb4") {
119+
continue
120+
}
121+
front = append(front, cs)
122+
}
123+
ordered = front
124+
}
125+
126+
return strings.Join(ordered, ",")
115127
}
116128

117129
// AdaptivePostgresqlDsn convert postgres dsn to kv string

0 commit comments

Comments
 (0)