Skip to content

Commit 2e5c5ac

Browse files
authored
Support DSN without 'user:password@' (#888)
* correctly parseDSN when username is not present * one more test caset * fixing golangci-lint errors * added collation to README.md * fixing incorrect comment
1 parent c607c3c commit 2e5c5ac

File tree

4 files changed

+25
-8
lines changed

4 files changed

+25
-8
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ Configuration options can be provided by the standard DSN (Data Source Name).
368368
[user[:password]@]addr[/db[?param=X]]
369369
```
370370

371+
#### `collation`
372+
373+
Set a collation during the Auth handshake.
374+
375+
| Type | Default | Example |
376+
| --------- | --------------- | ----------------------------------------------------- |
377+
| string | utf8_general_ci | user:pass@localhost/mydb?collation=latin1_general_ci |
378+
371379
#### `compress`
372380

373381
Enable compression between the client and the server. Valid values are 'zstd','zlib','uncompressed'.

client/auth.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,8 @@ func (c *Conn) writeAuthHandshake() error {
285285
return fmt.Errorf("invalid collation name %s", collationName)
286286
}
287287

288-
// the MySQL protocol calls for the collation id to be sent as 1, where only the
289-
// lower 8 bits are used in this field. But wireshark shows that the first byte of
290-
// the 23 bytes of filler is used to send the right middle 8 bits of the collation id.
291-
// see https://github.com/mysql/mysql-server/pull/541
288+
// the MySQL protocol calls for the collation id to be sent as 1 byte, where only the
289+
// lower 8 bits are used in this field.
292290
data[12] = byte(collation.ID & 0xff)
293291

294292
// SSL Connection Request Packet

driver/driver.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"io"
1212
"net/url"
1313
"regexp"
14+
"strings"
1415
"sync"
1516
"time"
1617

@@ -24,6 +25,7 @@ var customTLSMutex sync.Mutex
2425

2526
// Map of dsn address (makes more sense than full dsn?) to tls Config
2627
var (
28+
dsnRegex = regexp.MustCompile("@[^@]+/[^@/]+")
2729
customTLSConfigMap = make(map[string]*tls.Config)
2830
options = make(map[string]DriverOption)
2931

@@ -55,13 +57,16 @@ type connInfo struct {
5557
//
5658
// Optional parameters are supported in the standard DSN form
5759
func parseDSN(dsn string) (connInfo, error) {
58-
var matchErr error
5960
ci := connInfo{}
6061

6162
// If a "/" occurs after "@" and then no more "@" or "/" occur after that
62-
ci.standardDSN, matchErr = regexp.MatchString("@[^@]+/[^@/]+", dsn)
63-
if matchErr != nil {
64-
return ci, errors.Errorf("invalid dsn, must be user:password@addr[/db[?param=X]]")
63+
if strings.Contains(dsn, "@") {
64+
ci.standardDSN = dsnRegex.MatchString(dsn)
65+
} else {
66+
// when the `@` char is not present in the dsn, then look for `/` as the db separator
67+
// to indicate a standard DSN. The legacy form uses the `?` char as the db separator.
68+
// If neither `/` or `?` are in the dsn, simply treat the dsn as the legacy form.
69+
ci.standardDSN = strings.Contains(dsn, "/")
6570
}
6671

6772
// Add a prefix so we can parse with url.Parse

driver/driver_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ func TestParseDSN(t *testing.T) {
9292
"user:[email protected]/db?readTimeout=1m": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"readTimeout": []string{"1m"}}},
9393
"user:[email protected]/db?writeTimeout=1m": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"writeTimeout": []string{"1m"}}},
9494
"user:[email protected]/db?compress=zlib": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"compress": []string{"zlib"}}},
95+
96+
// per the documentation in the README, the 'user:password@' is optional as are the '/db?param=X' portions of the DSN
97+
"6.domain.com": {standardDSN: false, addr: "6.domain.com", user: "", password: "", db: "", params: url.Values{}},
98+
"7.domain.com?db": {standardDSN: false, addr: "7.domain.com", user: "", password: "", db: "db", params: url.Values{}},
99+
"8.domain.com/db": {standardDSN: true, addr: "8.domain.com", user: "", password: "", db: "db", params: url.Values{}},
100+
"9.domain.com/db?compress=zlib": {standardDSN: true, addr: "9.domain.com", user: "", password: "", db: "db", params: url.Values{"compress": []string{"zlib"}}},
95101
}
96102

97103
for supplied, expected := range testDSNs {

0 commit comments

Comments
 (0)