@@ -8,7 +8,122 @@ import (
88
99// AdaptiveMysqlDsn adaptation of various mysql format dsn address
1010func AdaptiveMysqlDsn (dsn string ) string {
11- return strings .ReplaceAll (dsn , "mysql://" , "" )
11+ // remove optional scheme prefix
12+ dsn = strings .ReplaceAll (dsn , "mysql://" , "" )
13+
14+ dsn = ensureNetworkAddress (dsn )
15+ return ensureCharsetAndCollation (dsn )
16+ }
17+
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+ }
48+
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+ }
82+
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 , "," )
12127}
13128
14129// AdaptivePostgresqlDsn convert postgres dsn to kv string
0 commit comments