From e179e1d4feeb952008a88c90e8c9bdbf3789bc2e Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 21 Oct 2024 09:22:53 +0800 Subject: [PATCH] fix(database/gdb): unix socket connection support for mysql (#3872) --- contrib/drivers/mysql/mysql_open.go | 33 +++++++++++-------- .../mysql/mysql_z_unit_internal_test.go | 30 +++++++++++++++++ database/gdb/gdb_z_mysql_internal_test.go | 18 +++++++++- 3 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 contrib/drivers/mysql/mysql_z_unit_internal_test.go diff --git a/contrib/drivers/mysql/mysql_open.go b/contrib/drivers/mysql/mysql_open.go index 4c53fcf39..cd5f74b11 100644 --- a/contrib/drivers/mysql/mysql_open.go +++ b/contrib/drivers/mysql/mysql_open.go @@ -9,7 +9,6 @@ package mysql import ( "database/sql" "fmt" - "net/url" "strings" @@ -23,9 +22,21 @@ import ( // Note that it converts time.Time argument to local timezone in default. func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { var ( - source string + source = configNodeToSource(config) underlyingDriverName = "mysql" ) + if db, err = sql.Open(underlyingDriverName, source); err != nil { + err = gerror.WrapCodef( + gcode.CodeDbOperationError, err, + `sql.Open failed for driver "%s" by source "%s"`, underlyingDriverName, source, + ) + return nil, err + } + return +} + +func configNodeToSource(config *gdb.ConfigNode) string { + var source string // [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] if config.Link != "" { // ============================================================================ @@ -37,10 +48,13 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { source, _ = gregex.ReplaceString(`/([\w\.\-]+)+`, "/"+config.Name, source) } } else { - // TODO: Do not set charset when charset is not specified (in v2.5.0) + var portStr string + if config.Port != "" { + portStr = ":" + config.Port + } source = fmt.Sprintf( - "%s:%s@%s(%s:%s)/%s?charset=%s", - config.User, config.Pass, config.Protocol, config.Host, config.Port, config.Name, config.Charset, + "%s:%s@%s(%s%s)/%s?charset=%s", + config.User, config.Pass, config.Protocol, config.Host, portStr, config.Name, config.Charset, ) if config.Timezone != "" { if strings.Contains(config.Timezone, "/") { @@ -52,12 +66,5 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { source = fmt.Sprintf("%s&%s", source, config.Extra) } } - if db, err = sql.Open(underlyingDriverName, source); err != nil { - err = gerror.WrapCodef( - gcode.CodeDbOperationError, err, - `sql.Open failed for driver "%s" by source "%s"`, underlyingDriverName, source, - ) - return nil, err - } - return + return source } diff --git a/contrib/drivers/mysql/mysql_z_unit_internal_test.go b/contrib/drivers/mysql/mysql_z_unit_internal_test.go new file mode 100644 index 000000000..b8868f6ca --- /dev/null +++ b/contrib/drivers/mysql/mysql_z_unit_internal_test.go @@ -0,0 +1,30 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package mysql + +import ( + "testing" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_configNodeToSource(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + configNode := &gdb.ConfigNode{ + Host: "/tmp/mysql.sock", + Port: "", + User: "username", + Pass: "password", + Name: "dbname", + Type: "mysql", + Protocol: "unix", + } + source := configNodeToSource(configNode) + t.Assert(source, "username:password@unix(/tmp/mysql.sock)/dbname?charset=") + }) +} diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 2f6947555..a2428dbee 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -232,7 +232,7 @@ func Test_parseConfigNodeLink_WithType(t *testing.T) { t.Assert(newNode.Charset, defaultCharset) t.Assert(newNode.Protocol, `tcp`) }) - // #3755 + // https://github.com/gogf/gf/issues/3755 gtest.C(t, func(t *gtest.T) { node := &ConfigNode{ Link: "mysql:user:pwd@tcp(rdsid.mysql.rds.aliyuncs.com)/dbname?charset=utf8&loc=Local", @@ -248,6 +248,22 @@ func Test_parseConfigNodeLink_WithType(t *testing.T) { t.Assert(newNode.Charset, `utf8`) t.Assert(newNode.Protocol, `tcp`) }) + // https://github.com/gogf/gf/issues/3862 + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: "mysql:username:password@unix(/tmp/mysql.sock)/dbname", + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `username`) + t.Assert(newNode.Pass, `password`) + t.Assert(newNode.Host, `/tmp/mysql.sock`) + t.Assert(newNode.Port, ``) + t.Assert(newNode.Name, `dbname`) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, `utf8`) + t.Assert(newNode.Protocol, `unix`) + }) } func Test_Func_doQuoteWord(t *testing.T) {