Skip to content

Commit e4a654a

Browse files
committed
fix logger race
1 parent c9f41c0 commit e4a654a

File tree

6 files changed

+89
-67
lines changed

6 files changed

+89
-67
lines changed

Diff for: AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Daniël van Eeden <git at myname.nl>
3535
Dave Protasowski <dprotaso at gmail.com>
3636
Dirkjan Bussink <d.bussink at gmail.com>
3737
DisposaBoy <disposaboy at dby.me>
38+
Dmitry Titov <[email protected]>
3839
Egor Smolyakov <egorsmkv at gmail.com>
3940
Erwan Martin <hello at erwan.io>
4041
Evan Elias <evan at skeema.net>

Diff for: driver_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2141,7 +2141,7 @@ func TestInsertRetrieveEscapedData(t *testing.T) {
21412141
func TestUnixSocketAuthFail(t *testing.T) {
21422142
runTests(t, dsn, func(dbt *DBTest) {
21432143
// Save the current logger so we can restore it.
2144-
oldLogger := defaultLogger
2144+
oldLogger := getLogger()
21452145

21462146
// Set a new logger so we can capture its output.
21472147
buffer := bytes.NewBuffer(make([]byte, 0, 64))

Diff for: dsn.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func NewConfig() *Config {
8989
cfg := &Config{
9090
Loc: time.UTC,
9191
MaxAllowedPacket: defaultMaxAllowedPacket,
92-
Logger: defaultLogger,
92+
Logger: getLogger(),
9393
AllowNativePasswords: true,
9494
CheckConnLiveness: true,
9595
}
@@ -203,7 +203,7 @@ func (cfg *Config) normalize() error {
203203
}
204204

205205
if cfg.Logger == nil {
206-
cfg.Logger = defaultLogger
206+
cfg.Logger = getLogger()
207207
}
208208

209209
return nil

Diff for: dsn_test.go

+66-59
Original file line numberDiff line numberDiff line change
@@ -17,67 +17,74 @@ import (
1717
"time"
1818
)
1919

20-
var testDSNs = []struct {
20+
var testDSNs []struct {
2121
in string
2222
out *Config
23-
}{{
24-
"username:password@protocol(address)/dbname?param=value",
25-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
26-
}, {
27-
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
28-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true},
29-
}, {
30-
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
31-
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true, MultiStatements: true},
32-
}, {
33-
"user@unix(/path/to/socket)/dbname?charset=utf8",
34-
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
35-
}, {
36-
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
37-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"},
38-
}, {
39-
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
40-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8mb4", "utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"},
41-
}, {
42-
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
43-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, Logger: defaultLogger, AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
44-
}, {
45-
"user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0&allowFallbackToPlaintext=true",
46-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: 0, Logger: defaultLogger, AllowFallbackToPlaintext: true, AllowNativePasswords: false, CheckConnLiveness: false},
47-
}, {
48-
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
49-
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
50-
}, {
51-
"/dbname",
52-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
53-
}, {
54-
"/dbname%2Fwithslash",
55-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname/withslash", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
56-
}, {
57-
"@/",
58-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
59-
}, {
60-
"/",
61-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
62-
}, {
63-
"",
64-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
65-
}, {
66-
"user:p@/ssword@/",
67-
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
68-
}, {
69-
"unix/?arg=%2Fsome%2Fpath.ext",
70-
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
71-
}, {
72-
"tcp(127.0.0.1)/dbname",
73-
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
74-
}, {
75-
"tcp(de:ad:be:ef::ca:fe)/dbname",
76-
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
77-
}, {
78-
"user:password@/dbname?loc=UTC&timeout=30s&parseTime=true&timeTruncate=1h",
79-
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30 * time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, timeTruncate: time.Hour},
80-
},
23+
}
24+
25+
func init() {
26+
testDSNs = []struct {
27+
in string
28+
out *Config
29+
}{{
30+
"username:password@protocol(address)/dbname?param=value",
31+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
32+
}, {
33+
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
34+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true},
35+
}, {
36+
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
37+
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true, MultiStatements: true},
38+
}, {
39+
"user@unix(/path/to/socket)/dbname?charset=utf8",
40+
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
41+
}, {
42+
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
43+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"},
44+
}, {
45+
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
46+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", charsets: []string{"utf8mb4", "utf8"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"},
47+
}, {
48+
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
49+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, Logger: getLogger(), AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
50+
}, {
51+
"user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0&allowFallbackToPlaintext=true",
52+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: 0, Logger: getLogger(), AllowFallbackToPlaintext: true, AllowNativePasswords: false, CheckConnLiveness: false},
53+
}, {
54+
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
55+
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
56+
}, {
57+
"/dbname",
58+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
59+
}, {
60+
"/dbname%2Fwithslash",
61+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname/withslash", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
62+
}, {
63+
"@/",
64+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
65+
}, {
66+
"/",
67+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
68+
}, {
69+
"",
70+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
71+
}, {
72+
"user:p@/ssword@/",
73+
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
74+
}, {
75+
"unix/?arg=%2Fsome%2Fpath.ext",
76+
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
77+
}, {
78+
"tcp(127.0.0.1)/dbname",
79+
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
80+
}, {
81+
"tcp(de:ad:be:ef::ca:fe)/dbname",
82+
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true},
83+
}, {
84+
"user:password@/dbname?loc=UTC&timeout=30s&parseTime=true&timeTruncate=1h",
85+
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30 * time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: getLogger(), AllowNativePasswords: true, CheckConnLiveness: true, timeTruncate: time.Hour},
86+
},
87+
}
8188
}
8289

8390
func TestDSNParser(t *testing.T) {

Diff for: errors.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"log"
1515
"os"
16+
"sync/atomic"
1617
)
1718

1819
// Various errors the driver might return. Can change between driver versions.
@@ -37,7 +38,11 @@ var (
3738
errBadConnNoWrite = errors.New("bad connection")
3839
)
3940

40-
var defaultLogger = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime))
41+
var defaultLogger atomic.Pointer[Logger]
42+
43+
func init() {
44+
SetLogger(Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime)))
45+
}
4146

4247
// Logger is used to log critical error messages.
4348
type Logger interface {
@@ -56,10 +61,19 @@ func SetLogger(logger Logger) error {
5661
if logger == nil {
5762
return errors.New("logger is nil")
5863
}
59-
defaultLogger = logger
64+
defaultLogger.Store(&logger)
6065
return nil
6166
}
6267

68+
func getLogger() Logger {
69+
l := defaultLogger.Load()
70+
if l == nil {
71+
return &NopLogger{}
72+
}
73+
74+
return *l
75+
}
76+
6377
// MySQLError is an error type which represents a single MySQL error
6478
type MySQLError struct {
6579
Number uint16

Diff for: errors_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import (
1616
)
1717

1818
func TestErrorsSetLogger(t *testing.T) {
19-
previous := defaultLogger
19+
previous := getLogger()
2020
defer func() {
21-
defaultLogger = previous
21+
SetLogger(previous)
2222
}()
2323

2424
// set up logger
@@ -28,7 +28,7 @@ func TestErrorsSetLogger(t *testing.T) {
2828

2929
// print
3030
SetLogger(logger)
31-
defaultLogger.Print("test")
31+
getLogger().Print("test")
3232

3333
// check result
3434
if actual := buffer.String(); actual != expected {

0 commit comments

Comments
 (0)