From d9f59f904a68595f382de21cd194b1ac13e0ff68 Mon Sep 17 00:00:00 2001 From: David Vilaverde Date: Wed, 12 Jun 2024 08:11:38 -0400 Subject: [PATCH 1/5] correctly parseDSN when username is not present --- driver/driver.go | 14 ++++++++++---- driver/driver_test.go | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/driver/driver.go b/driver/driver.go index f7e3972e7..ab573fc79 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -11,6 +11,7 @@ import ( "io" "net/url" "regexp" + "strings" "sync" "time" @@ -24,6 +25,7 @@ var customTLSMutex sync.Mutex // Map of dsn address (makes more sense than full dsn?) to tls Config var ( + dsnRegex = regexp.MustCompile("@[^@]+/[^@/]+") customTLSConfigMap = make(map[string]*tls.Config) options = make(map[string]DriverOption) @@ -55,13 +57,17 @@ type connInfo struct { // // Optional parameters are supported in the standard DSN form func parseDSN(dsn string) (connInfo, error) { - var matchErr error + ci := connInfo{} // If a "/" occurs after "@" and then no more "@" or "/" occur after that - ci.standardDSN, matchErr = regexp.MatchString("@[^@]+/[^@/]+", dsn) - if matchErr != nil { - return ci, errors.Errorf("invalid dsn, must be user:password@addr[/db[?param=X]]") + if strings.Contains(dsn, "@") { + ci.standardDSN = dsnRegex.MatchString(dsn) + } else { + // when the `@` char is not present in the dsn, then look for `/` as the db separator + // to indicate a standard DSN. The legacy form uses the `?` char as the db separator. + // If neither `/` or `?` are in the dsn, simply treat the dsn as the legacy form. + ci.standardDSN = strings.Contains(dsn, "/") } // Add a prefix so we can parse with url.Parse diff --git a/driver/driver_test.go b/driver/driver_test.go index 1c21bd0e3..6f74d43d2 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -92,6 +92,11 @@ func TestParseDSN(t *testing.T) { "user:password@5.domain.com/db?readTimeout=1m": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"readTimeout": []string{"1m"}}}, "user:password@5.domain.com/db?writeTimeout=1m": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"writeTimeout": []string{"1m"}}}, "user:password@5.domain.com/db?compress=zlib": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"compress": []string{"zlib"}}}, + + // per the documentation in the README, the 'user:password@' is optional as are the '/db?param=X' portions of the DSN + "2.domain.com": {standardDSN: false, addr: "2.domain.com", user: "", password: "", db: "", params: url.Values{}}, + "2.domain.com/db": {standardDSN: true, addr: "2.domain.com", user: "", password: "", db: "db", params: url.Values{}}, + "5.domain.com/db?compress=zlib": {standardDSN: true, addr: "5.domain.com", user: "", password: "", db: "db", params: url.Values{"compress": []string{"zlib"}}}, } for supplied, expected := range testDSNs { From 07000350f9f7017ecc808e54794e2ae1b8170e5b Mon Sep 17 00:00:00 2001 From: David Vilaverde Date: Wed, 12 Jun 2024 08:14:25 -0400 Subject: [PATCH 2/5] one more test caset --- driver/driver_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/driver/driver_test.go b/driver/driver_test.go index 6f74d43d2..bf4eef87b 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -94,9 +94,10 @@ func TestParseDSN(t *testing.T) { "user:password@5.domain.com/db?compress=zlib": {standardDSN: true, addr: "5.domain.com", user: "user", password: "password", db: "db", params: url.Values{"compress": []string{"zlib"}}}, // per the documentation in the README, the 'user:password@' is optional as are the '/db?param=X' portions of the DSN - "2.domain.com": {standardDSN: false, addr: "2.domain.com", user: "", password: "", db: "", params: url.Values{}}, - "2.domain.com/db": {standardDSN: true, addr: "2.domain.com", user: "", password: "", db: "db", params: url.Values{}}, - "5.domain.com/db?compress=zlib": {standardDSN: true, addr: "5.domain.com", user: "", password: "", db: "db", params: url.Values{"compress": []string{"zlib"}}}, + "6.domain.com": {standardDSN: false, addr: "6.domain.com", user: "", password: "", db: "", params: url.Values{}}, + "7.domain.com?db": {standardDSN: false, addr: "7.domain.com", user: "", password: "", db: "db", params: url.Values{}}, + "8.domain.com/db": {standardDSN: true, addr: "8.domain.com", user: "", password: "", db: "db", params: url.Values{}}, + "9.domain.com/db?compress=zlib": {standardDSN: true, addr: "9.domain.com", user: "", password: "", db: "db", params: url.Values{"compress": []string{"zlib"}}}, } for supplied, expected := range testDSNs { From 572dfc7a2dcdc697c47a7a65d7c88b27351f0bc2 Mon Sep 17 00:00:00 2001 From: David Vilaverde Date: Wed, 12 Jun 2024 08:19:44 -0400 Subject: [PATCH 3/5] fixing golangci-lint errors --- driver/driver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/driver/driver.go b/driver/driver.go index ab573fc79..d2343fdad 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -57,7 +57,6 @@ type connInfo struct { // // Optional parameters are supported in the standard DSN form func parseDSN(dsn string) (connInfo, error) { - ci := connInfo{} // If a "/" occurs after "@" and then no more "@" or "/" occur after that From e876e5bfe2c20962886207078f47d2ca2e650381 Mon Sep 17 00:00:00 2001 From: David Vilaverde Date: Wed, 12 Jun 2024 08:37:30 -0400 Subject: [PATCH 4/5] added collation to README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index b3042e7e7..d5cd6e4a5 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,14 @@ Configuration options can be provided by the standard DSN (Data Source Name). [user[:password]@]addr[/db[?param=X]] ``` +#### `collation` + +Set a collation during the Auth handshake. + +| Type | Default | Example | +| --------- | --------------- | ----------------------------------------------------- | +| string | utf8_general_ci | user:pass@localhost/mydb?collation=latin1_general_ci | + #### `compress` Enable compression between the client and the server. Valid values are 'zstd','zlib','uncompressed'. From 286d15e205c6f842f99ae9ae1ebd50611491480a Mon Sep 17 00:00:00 2001 From: David Vilaverde Date: Wed, 12 Jun 2024 18:55:53 -0400 Subject: [PATCH 5/5] fixing incorrect comment --- client/auth.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/auth.go b/client/auth.go index ab6fe75e7..fc65332e0 100644 --- a/client/auth.go +++ b/client/auth.go @@ -285,10 +285,8 @@ func (c *Conn) writeAuthHandshake() error { return fmt.Errorf("invalid collation name %s", collationName) } - // the MySQL protocol calls for the collation id to be sent as 1, where only the - // lower 8 bits are used in this field. But wireshark shows that the first byte of - // the 23 bytes of filler is used to send the right middle 8 bits of the collation id. - // see https://github.com/mysql/mysql-server/pull/541 + // the MySQL protocol calls for the collation id to be sent as 1 byte, where only the + // lower 8 bits are used in this field. data[12] = byte(collation.ID & 0xff) // SSL Connection Request Packet