Skip to content

Commit 82cddef

Browse files
committedMar 15, 2016
Add support for connection attributes.
This sets attribute _client_name with the value "Go MySQL Driver" Also sets _os, _platform, _pid and program_name by default. This also decodes the uppper two bytes of the capability flags. The dsn_test.go only tests for one attribute because there is no guaranteed sort order for a map and Printf %+v as used by TestDSNParser().
1 parent 66312f7 commit 82cddef

File tree

6 files changed

+143
-30
lines changed

6 files changed

+143
-30
lines changed
 

‎README.md

+10
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ Sets the collation used for client-server interaction on connection. In contrast
169169

170170
A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
171171

172+
##### `connattrs`
173+
174+
```
175+
Type: comma seperated string of name/value pairs
176+
Valid Values: (<name1>=<value1>,<name2>=<value2>,...)
177+
Default: none
178+
```
179+
180+
Sends custom connection attributes to the server.
181+
172182
##### `clientFoundRows`
173183

174184
```

‎driver.go

+13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import (
2020
"database/sql"
2121
"database/sql/driver"
2222
"net"
23+
"os"
24+
"os/user"
25+
"strconv"
2326
)
2427

2528
// MySQLDriver is exported to make the driver directly accessible.
@@ -30,6 +33,10 @@ type MySQLDriver struct{}
3033
// Custom dial functions must be registered with RegisterDial
3134
type DialFunc func(addr string) (net.Conn, error)
3235

36+
var pid string
37+
var os_user string
38+
var os_user_full string
39+
3340
var dials map[string]DialFunc
3441

3542
// RegisterDial registers a custom dial function. It can then be used by the
@@ -163,5 +170,11 @@ func handleAuthResult(mc *mysqlConn, cipher []byte) error {
163170
}
164171

165172
func init() {
173+
pid = strconv.Itoa(os.Getpid())
174+
os_user_entry, err := user.Current()
175+
if err == nil {
176+
os_user_full = os_user_entry.Name
177+
os_user = os_user_entry.Username
178+
}
166179
sql.Register("mysql", &MySQLDriver{})
167180
}

‎driver_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
)
2929

3030
var (
31-
user string
31+
dbuser string
3232
pass string
3333
prot string
3434
addr string
@@ -57,13 +57,13 @@ func init() {
5757
}
5858
return defaultValue
5959
}
60-
user = env("MYSQL_TEST_USER", "root")
60+
dbuser = env("MYSQL_TEST_USER", "root")
6161
pass = env("MYSQL_TEST_PASS", "")
6262
prot = env("MYSQL_TEST_PROT", "tcp")
6363
addr = env("MYSQL_TEST_ADDR", "localhost:3306")
6464
dbname = env("MYSQL_TEST_DBNAME", "gotest")
6565
netAddr = fmt.Sprintf("%s(%s)", prot, addr)
66-
dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", user, pass, netAddr, dbname)
66+
dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", dbuser, pass, netAddr, dbname)
6767
c, err := net.Dial(prot, addr)
6868
if err == nil {
6969
available = true
@@ -1693,7 +1693,7 @@ func TestCustomDial(t *testing.T) {
16931693
return net.Dial(prot, addr)
16941694
})
16951695

1696-
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mydial(%s)/%s?timeout=30s&strict=true", user, pass, addr, dbname))
1696+
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mydial(%s)/%s?timeout=30s&strict=true", dbuser, pass, addr, dbname))
16971697
if err != nil {
16981698
t.Fatalf("error connecting: %s", err.Error())
16991699
}
@@ -1790,7 +1790,7 @@ func TestUnixSocketAuthFail(t *testing.T) {
17901790
}
17911791
}
17921792
t.Logf("socket: %s", socket)
1793-
badDSN := fmt.Sprintf("%s:%s@unix(%s)/%s?timeout=30s&strict=true", user, badPass, socket, dbname)
1793+
badDSN := fmt.Sprintf("%s:%s@unix(%s)/%s?timeout=30s&strict=true", dbuser, badPass, socket, dbname)
17941794
db, err := sql.Open("mysql", badDSN)
17951795
if err != nil {
17961796
t.Fatalf("error connecting: %s", err.Error())

‎dsn.go

+42-10
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,16 @@ type Config struct {
4242
ReadTimeout time.Duration // I/O read timeout
4343
WriteTimeout time.Duration // I/O write timeout
4444

45-
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
46-
AllowCleartextPasswords bool // Allows the cleartext client side plugin
47-
AllowOldPasswords bool // Allows the old insecure password method
48-
ClientFoundRows bool // Return number of matching rows instead of rows changed
49-
ColumnsWithAlias bool // Prepend table alias to column names
50-
InterpolateParams bool // Interpolate placeholders into query string
51-
MultiStatements bool // Allow multiple statements in one query
52-
ParseTime bool // Parse time values to time.Time
53-
Strict bool // Return warnings as errors
45+
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
46+
AllowCleartextPasswords bool // Allows the cleartext client side plugin
47+
AllowOldPasswords bool // Allows the old insecure password method
48+
ClientFoundRows bool // Return number of matching rows instead of rows changed
49+
ColumnsWithAlias bool // Prepend table alias to column names
50+
InterpolateParams bool // Interpolate placeholders into query string
51+
MultiStatements bool // Allow multiple statements in one query
52+
ParseTime bool // Parse time values to time.Time
53+
Strict bool // Return warnings as errors
54+
ConnAttrs map[string]string // Connection Attributes
5455
}
5556

5657
// FormatDSN formats the given Config into a DSN string which can be passed to
@@ -222,6 +223,27 @@ func (cfg *Config) FormatDSN() string {
222223
buf.WriteString(cfg.WriteTimeout.String())
223224
}
224225

226+
if len(cfg.ConnAttrs) != 0 {
227+
if hasParam {
228+
buf.WriteString("&connattrs=(")
229+
} else {
230+
hasParam = true
231+
buf.WriteString("?connattrs=(")
232+
}
233+
firstAttr := true
234+
for attrname, attrvalue := range cfg.ConnAttrs {
235+
if firstAttr {
236+
firstAttr = false
237+
} else {
238+
buf.WriteString(",")
239+
}
240+
buf.WriteString(attrname)
241+
buf.WriteString("=")
242+
buf.WriteString(attrvalue)
243+
}
244+
buf.WriteString(")")
245+
}
246+
225247
// other params
226248
if cfg.Params != nil {
227249
for param, value := range cfg.Params {
@@ -496,7 +518,17 @@ func parseDSNParams(cfg *Config, params string) (err error) {
496518
if err != nil {
497519
return
498520
}
499-
521+
case "connattrs":
522+
if cfg.ConnAttrs == nil {
523+
cfg.ConnAttrs = make(map[string]string)
524+
}
525+
for _, conn_v := range strings.Split(strings.Trim(value, "()"), ",") {
526+
attr := strings.SplitN(conn_v, "=", 2)
527+
if len(attr) != 2 {
528+
return fmt.Errorf("Invalid connection attribute: %s", conn_v)
529+
}
530+
cfg.ConnAttrs[attr[0]] = attr[1]
531+
}
500532
default:
501533
// lazy init
502534
if cfg.Params == nil {

‎dsn_test.go

+15-14
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,21 @@ var testDSNs = []struct {
1919
in string
2020
out string
2121
}{
22-
{"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
23-
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
24-
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false}"},
25-
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
26-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
27-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
28-
{"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
29-
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
30-
{"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
31-
{"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
32-
{"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
33-
{"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
34-
{"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
35-
{"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"},
22+
{"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
23+
{"username:password@protocol(address)/dbname?param=value&connattrs=(foo=bar)", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[foo:bar]}"},
24+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
25+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false ConnAttrs:map[]}"},
26+
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
27+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
28+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
29+
{"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls:<nil> Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
30+
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
31+
{"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
32+
{"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
33+
{"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
34+
{"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
35+
{"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
36+
{"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls:<nil> Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false ConnAttrs:map[]}"},
3637
}
3738

3839
func TestDSNParser(t *testing.T) {

‎packets.go

+58-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import (
1717
"fmt"
1818
"io"
1919
"math"
20+
"os"
21+
"path"
22+
"runtime"
2023
"time"
2124
)
2225

@@ -175,10 +178,15 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
175178
if len(data) > pos {
176179
// character set [1 byte]
177180
// status flags [2 bytes]
181+
pos += 1 + 2
182+
178183
// capability flags (upper 2 bytes) [2 bytes]
184+
mc.flags |= (clientFlag(binary.LittleEndian.Uint16(data[pos:pos+2])) << 16)
185+
pos += 2
186+
179187
// length of auth-plugin-data [1 byte]
180188
// reserved (all [00]) [10 bytes]
181-
pos += 1 + 2 + 2 + 1 + 10
189+
pos += 1 + 10
182190

183191
// second part of the password cipher [mininum 13 bytes],
184192
// where len=MAX(13, length of auth-plugin-data - 8)
@@ -246,6 +254,40 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
246254

247255
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1
248256

257+
// Default connection attributes
258+
attrlen := 0
259+
var attrs map[string]string
260+
if mc.flags&clientConnectAttrs != 0 {
261+
clientFlags |= clientConnectAttrs
262+
263+
attrs = map[string]string{
264+
"_os": runtime.GOOS,
265+
"_client_name": "Go-MySQL-Driver",
266+
"_pid": pid,
267+
"_platform": runtime.GOARCH,
268+
"program_name": path.Base(os.Args[0]),
269+
}
270+
if len(os_user_full) > 0 {
271+
attrs["_os_user_full"] = os_user_full
272+
}
273+
if len(os_user) > 0 {
274+
attrs["_os_user"] = os_user
275+
}
276+
277+
// Merge the custom attributes and the default attributes
278+
for cfganame, cfgaval := range mc.cfg.ConnAttrs {
279+
attrs[cfganame] = cfgaval
280+
}
281+
282+
for attrname, attrvalue := range attrs {
283+
attrlen += len(attrname) + len(attrvalue)
284+
// one byte to store attrname length and one byte to store attrvalue length
285+
attrlen += 2
286+
}
287+
288+
pktLen += attrlen + 1 // one byte to store the total length of attrs
289+
}
290+
249291
// To specify a db name
250292
if n := len(mc.cfg.DBName); n > 0 {
251293
clientFlags |= clientConnectWithDB
@@ -326,6 +368,21 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
326368
// Assume native client during response
327369
pos += copy(data[pos:], "mysql_native_password")
328370
data[pos] = 0x00
371+
pos++
372+
373+
// Connection attributes
374+
if attrlen > 0 {
375+
data[pos] = byte(attrlen)
376+
pos++
377+
378+
for attrname, attrvalue := range attrs {
379+
data[pos] = byte(len(attrname))
380+
pos += 1 + copy(data[pos+1:], attrname)
381+
382+
data[pos] = byte(len(attrvalue))
383+
pos += 1 + copy(data[pos+1:], attrvalue)
384+
}
385+
}
329386

330387
// Send Auth packet
331388
return mc.writePacket(data)

0 commit comments

Comments
 (0)
Please sign in to comment.