• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-sql-driver / mysql / 14570841909

21 Apr 2025 09:10AM UTC coverage: 83.0% (-0.01%) from 83.01%
14570841909

Pull #1692

github

web-flow
Merge branch 'master' into clientDeprecateEOF
Pull Request #1692: MariaDB metadata skipping

170 of 203 new or added lines in 6 files covered. (83.74%)

3 existing lines in 1 file now uncovered.

3320 of 4000 relevant lines covered (83.0%)

2184086.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.45
/dsn.go
1
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
2
//
3
// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
4
//
5
// This Source Code Form is subject to the terms of the Mozilla Public
6
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
7
// You can obtain one at http://mozilla.org/MPL/2.0/.
8

9
package mysql
10

11
import (
12
        "bytes"
13
        "context"
14
        "crypto/rsa"
15
        "crypto/tls"
16
        "errors"
17
        "fmt"
18
        "math/big"
19
        "net"
20
        "net/url"
21
        "sort"
22
        "strconv"
23
        "strings"
24
        "time"
25
)
26

27
var (
28
        errInvalidDSNUnescaped       = errors.New("invalid DSN: did you forget to escape a param value?")
29
        errInvalidDSNAddr            = errors.New("invalid DSN: network address not terminated (missing closing brace)")
30
        errInvalidDSNNoSlash         = errors.New("invalid DSN: missing the slash separating the database name")
31
        errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
32
)
33

34
// Config is a configuration parsed from a DSN string.
35
// If a new Config is created instead of being parsed from a DSN string,
36
// the NewConfig function should be used, which sets default values.
37
type Config struct {
38
        // non boolean fields
39

40
        User                 string            // Username
41
        Passwd               string            // Password (requires User)
42
        Net                  string            // Network (e.g. "tcp", "tcp6", "unix". default: "tcp")
43
        Addr                 string            // Address (default: "127.0.0.1:3306" for "tcp" and "/tmp/mysql.sock" for "unix")
44
        DBName               string            // Database name
45
        Params               map[string]string // Connection parameters
46
        ConnectionAttributes string            // Connection Attributes, comma-delimited string of user-defined "key:value" pairs
47
        Collation            string            // Connection collation. When set, this will be set in SET NAMES <charset> COLLATE <collation> query
48
        Loc                  *time.Location    // Location for time.Time values
49
        MaxAllowedPacket     int               // Max packet size allowed
50
        ServerPubKey         string            // Server public key name
51
        TLSConfig            string            // TLS configuration name
52
        TLS                  *tls.Config       // TLS configuration, its priority is higher than TLSConfig
53
        Timeout              time.Duration     // Dial timeout
54
        ReadTimeout          time.Duration     // I/O read timeout
55
        WriteTimeout         time.Duration     // I/O write timeout
56
        Logger               Logger            // Logger
57
        // DialFunc specifies the dial function for creating connections
58
        DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
59

60
        // boolean fields
61

62
        AllowAllFiles            bool // Allow all files to be used with LOAD DATA LOCAL INFILE
63
        AllowCleartextPasswords  bool // Allows the cleartext client side plugin
64
        AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
65
        AllowNativePasswords     bool // Allows the native password authentication method
66
        AllowOldPasswords        bool // Allows the old insecure password method
67
        CheckConnLiveness        bool // Check connections for liveness before using them
68
        ClientFoundRows          bool // Return number of matching rows instead of rows changed
69
        ColumnsWithAlias         bool // Prepend table alias to column names
70
        InterpolateParams        bool // Interpolate placeholders into query string
71
        MultiStatements          bool // Allow multiple statements in one query
72
        ParseTime                bool // Parse time values to time.Time
73
        RejectReadOnly           bool // Reject read-only connections
74

75
        // unexported fields. new options should be come here.
76
        // boolean first. alphabetical order.
77

78
        compress bool // Enable zlib compression
79

80
        beforeConnect func(context.Context, *Config) error // Invoked before a connection is established
81
        pubKey        *rsa.PublicKey                       // Server public key
82
        timeTruncate  time.Duration                        // Truncate time.Time values to the specified duration
83
        charsets      []string                             // Connection charset. When set, this will be set in SET NAMES <charset> query
84
}
85

86
// Functional Options Pattern
87
// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
88
type Option func(*Config) error
89

90
// NewConfig creates a new Config and sets default values.
91
func NewConfig() *Config {
13,188✔
92
        cfg := &Config{
13,188✔
93
                Loc:                  time.UTC,
13,188✔
94
                MaxAllowedPacket:     defaultMaxAllowedPacket,
13,188✔
95
                Logger:               defaultLogger,
13,188✔
96
                AllowNativePasswords: true,
13,188✔
97
                CheckConnLiveness:    true,
13,188✔
98
        }
13,188✔
99
        return cfg
13,188✔
100
}
13,188✔
101

102
// Apply applies the given options to the Config object.
103
func (c *Config) Apply(opts ...Option) error {
32✔
104
        for _, opt := range opts {
64✔
105
                err := opt(c)
32✔
106
                if err != nil {
32✔
107
                        return err
×
108
                }
×
109
        }
110
        return nil
32✔
111
}
112

113
// TimeTruncate sets the time duration to truncate time.Time values in
114
// query parameters.
115
func TimeTruncate(d time.Duration) Option {
×
116
        return func(cfg *Config) error {
×
117
                cfg.timeTruncate = d
×
118
                return nil
×
119
        }
×
120
}
121

122
// BeforeConnect sets the function to be invoked before a connection is established.
123
func BeforeConnect(fn func(context.Context, *Config) error) Option {
32✔
124
        return func(cfg *Config) error {
64✔
125
                cfg.beforeConnect = fn
32✔
126
                return nil
32✔
127
        }
32✔
128
}
129

130
// EnableCompress sets the compression mode.
131
func EnableCompression(yes bool) Option {
×
132
        return func(cfg *Config) error {
×
133
                cfg.compress = yes
×
134
                return nil
×
135
        }
×
136
}
137

138
// Charset sets the connection charset and collation.
139
//
140
// charset is the connection charset.
141
// collation is the connection collation. It can be null or empty string.
142
//
143
// When collation is not specified, `SET NAMES <charset>` command is sent when the connection is established.
144
// When collation is specified, `SET NAMES <charset> COLLATE <collation>` command is sent when the connection is established.
145
func Charset(charset, collation string) Option {
×
146
        return func(cfg *Config) error {
×
147
                cfg.charsets = []string{charset}
×
148
                cfg.Collation = collation
×
149
                return nil
×
150
        }
×
151
}
152

153
func (cfg *Config) Clone() *Config {
192✔
154
        cp := *cfg
192✔
155
        if cp.TLS != nil {
224✔
156
                cp.TLS = cfg.TLS.Clone()
32✔
157
        }
32✔
158
        if len(cp.Params) > 0 {
224✔
159
                cp.Params = make(map[string]string, len(cfg.Params))
32✔
160
                for k, v := range cfg.Params {
64✔
161
                        cp.Params[k] = v
32✔
162
                }
32✔
163
        }
164
        if cfg.pubKey != nil {
224✔
165
                cp.pubKey = &rsa.PublicKey{
32✔
166
                        N: new(big.Int).Set(cfg.pubKey.N),
32✔
167
                        E: cfg.pubKey.E,
32✔
168
                }
32✔
169
        }
32✔
170
        return &cp
192✔
171
}
172

173
func (cfg *Config) normalize() error {
11,652✔
174
        if cfg.InterpolateParams && cfg.Collation != "" && unsafeCollations[cfg.Collation] {
11,684✔
175
                return errInvalidDSNUnsafeCollation
32✔
176
        }
32✔
177

178
        // Set default network if empty
179
        if cfg.Net == "" {
12,868✔
180
                cfg.Net = "tcp"
1,248✔
181
        }
1,248✔
182

183
        // Set default address if empty
184
        if cfg.Addr == "" {
12,836✔
185
                switch cfg.Net {
1,216✔
186
                case "tcp":
1,056✔
187
                        cfg.Addr = "127.0.0.1:3306"
1,056✔
188
                case "unix":
96✔
189
                        cfg.Addr = "/tmp/mysql.sock"
96✔
190
                default:
64✔
191
                        return errors.New("default addr for network '" + cfg.Net + "' unknown")
64✔
192
                }
193
        } else if cfg.Net == "tcp" {
19,944✔
194
                cfg.Addr = ensureHavePort(cfg.Addr)
9,540✔
195
        }
9,540✔
196

197
        if cfg.TLS == nil {
23,112✔
198
                switch cfg.TLSConfig {
11,556✔
199
                case "false", "":
10,436✔
200
                        // don't set anything
201
                case "true":
256✔
202
                        cfg.TLS = &tls.Config{}
256✔
203
                case "skip-verify":
416✔
204
                        cfg.TLS = &tls.Config{InsecureSkipVerify: true}
416✔
205
                case "preferred":
160✔
206
                        cfg.TLS = &tls.Config{InsecureSkipVerify: true}
160✔
207
                        cfg.AllowFallbackToPlaintext = true
160✔
208
                default:
288✔
209
                        cfg.TLS = getTLSConfigClone(cfg.TLSConfig)
288✔
210
                        if cfg.TLS == nil {
320✔
211
                                return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
32✔
212
                        }
32✔
213
                }
214
        }
215

216
        if cfg.TLS != nil && cfg.TLS.ServerName == "" && !cfg.TLS.InsecureSkipVerify {
11,812✔
217
                host, _, err := net.SplitHostPort(cfg.Addr)
288✔
218
                if err == nil {
576✔
219
                        cfg.TLS.ServerName = host
288✔
220
                }
288✔
221
        }
222

223
        if cfg.ServerPubKey != "" {
11,652✔
224
                cfg.pubKey = getServerPubKey(cfg.ServerPubKey)
128✔
225
                if cfg.pubKey == nil {
160✔
226
                        return errors.New("invalid value / unknown server pub key name: " + cfg.ServerPubKey)
32✔
227
                }
32✔
228
        }
229

230
        if cfg.Logger == nil {
11,684✔
231
                cfg.Logger = defaultLogger
192✔
232
        }
192✔
233

234
        return nil
11,492✔
235
}
236

237
func writeDSNParam(buf *bytes.Buffer, hasParam *bool, name, value string) {
3,904✔
238
        buf.Grow(1 + len(name) + 1 + len(value))
3,904✔
239
        if !*hasParam {
5,088✔
240
                *hasParam = true
1,184✔
241
                buf.WriteByte('?')
1,184✔
242
        } else {
3,904✔
243
                buf.WriteByte('&')
2,720✔
244
        }
2,720✔
245
        buf.WriteString(name)
3,904✔
246
        buf.WriteByte('=')
3,904✔
247
        buf.WriteString(value)
3,904✔
248
}
249

250
// FormatDSN formats the given Config into a DSN string which can be passed to
251
// the driver.
252
//
253
// Note: use [NewConnector] and [database/sql.OpenDB] to open a connection from a [*Config].
254
func (cfg *Config) FormatDSN() string {
2,336✔
255
        var buf bytes.Buffer
2,336✔
256

2,336✔
257
        // [username[:password]@]
2,336✔
258
        if len(cfg.User) > 0 {
3,648✔
259
                buf.WriteString(cfg.User)
1,312✔
260
                if len(cfg.Passwd) > 0 {
2,528✔
261
                        buf.WriteByte(':')
1,216✔
262
                        buf.WriteString(cfg.Passwd)
1,216✔
263
                }
1,216✔
264
                buf.WriteByte('@')
1,312✔
265
        }
266

267
        // [protocol[(address)]]
268
        if len(cfg.Net) > 0 {
4,640✔
269
                buf.WriteString(cfg.Net)
2,304✔
270
                if len(cfg.Addr) > 0 {
4,608✔
271
                        buf.WriteByte('(')
2,304✔
272
                        buf.WriteString(cfg.Addr)
2,304✔
273
                        buf.WriteByte(')')
2,304✔
274
                }
2,304✔
275
        }
276

277
        // /dbname
278
        buf.WriteByte('/')
2,336✔
279
        buf.WriteString(url.PathEscape(cfg.DBName))
2,336✔
280

2,336✔
281
        // [?param1=value1&...&paramN=valueN]
2,336✔
282
        hasParam := false
2,336✔
283

2,336✔
284
        if cfg.AllowAllFiles {
2,464✔
285
                hasParam = true
128✔
286
                buf.WriteString("?allowAllFiles=true")
128✔
287
        }
128✔
288

289
        if cfg.AllowCleartextPasswords {
2,464✔
290
                writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true")
128✔
291
        }
128✔
292

293
        if cfg.AllowFallbackToPlaintext {
2,464✔
294
                writeDSNParam(&buf, &hasParam, "allowFallbackToPlaintext", "true")
128✔
295
        }
128✔
296

297
        if !cfg.AllowNativePasswords {
2,464✔
298
                writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false")
128✔
299
        }
128✔
300

301
        if cfg.AllowOldPasswords {
2,464✔
302
                writeDSNParam(&buf, &hasParam, "allowOldPasswords", "true")
128✔
303
        }
128✔
304

305
        if !cfg.CheckConnLiveness {
2,464✔
306
                writeDSNParam(&buf, &hasParam, "checkConnLiveness", "false")
128✔
307
        }
128✔
308

309
        if cfg.ClientFoundRows {
2,464✔
310
                writeDSNParam(&buf, &hasParam, "clientFoundRows", "true")
128✔
311
        }
128✔
312

313
        if charsets := cfg.charsets; len(charsets) > 0 {
2,624✔
314
                writeDSNParam(&buf, &hasParam, "charset", strings.Join(charsets, ","))
288✔
315
        }
288✔
316

317
        if col := cfg.Collation; col != "" {
2,464✔
318
                writeDSNParam(&buf, &hasParam, "collation", col)
128✔
319
        }
128✔
320

321
        if cfg.ColumnsWithAlias {
2,528✔
322
                writeDSNParam(&buf, &hasParam, "columnsWithAlias", "true")
192✔
323
        }
192✔
324

325
        if cfg.ConnectionAttributes != "" {
2,464✔
326
                writeDSNParam(&buf, &hasParam, "connectionAttributes", url.QueryEscape(cfg.ConnectionAttributes))
128✔
327
        }
128✔
328

329
        if cfg.compress {
2,336✔
330
                writeDSNParam(&buf, &hasParam, "compress", "true")
×
331
        }
×
332

333
        if cfg.InterpolateParams {
2,368✔
334
                writeDSNParam(&buf, &hasParam, "interpolateParams", "true")
32✔
335
        }
32✔
336

337
        if cfg.Loc != time.UTC && cfg.Loc != nil {
2,432✔
338
                writeDSNParam(&buf, &hasParam, "loc", url.QueryEscape(cfg.Loc.String()))
96✔
339
        }
96✔
340

341
        if cfg.MultiStatements {
2,432✔
342
                writeDSNParam(&buf, &hasParam, "multiStatements", "true")
96✔
343
        }
96✔
344

345
        if cfg.ParseTime {
2,592✔
346
                writeDSNParam(&buf, &hasParam, "parseTime", "true")
256✔
347
        }
256✔
348

349
        if cfg.timeTruncate > 0 {
2,464✔
350
                writeDSNParam(&buf, &hasParam, "timeTruncate", cfg.timeTruncate.String())
128✔
351
        }
128✔
352

353
        if cfg.ReadTimeout > 0 {
2,464✔
354
                writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String())
128✔
355
        }
128✔
356

357
        if cfg.RejectReadOnly {
2,464✔
358
                writeDSNParam(&buf, &hasParam, "rejectReadOnly", "true")
128✔
359
        }
128✔
360

361
        if len(cfg.ServerPubKey) > 0 {
2,336✔
362
                writeDSNParam(&buf, &hasParam, "serverPubKey", url.QueryEscape(cfg.ServerPubKey))
×
363
        }
×
364

365
        if cfg.Timeout > 0 {
2,720✔
366
                writeDSNParam(&buf, &hasParam, "timeout", cfg.Timeout.String())
384✔
367
        }
384✔
368

369
        if len(cfg.TLSConfig) > 0 {
2,656✔
370
                writeDSNParam(&buf, &hasParam, "tls", url.QueryEscape(cfg.TLSConfig))
320✔
371
        }
320✔
372

373
        if cfg.WriteTimeout > 0 {
2,464✔
374
                writeDSNParam(&buf, &hasParam, "writeTimeout", cfg.WriteTimeout.String())
128✔
375
        }
128✔
376

377
        if cfg.MaxAllowedPacket != defaultMaxAllowedPacket {
2,592✔
378
                writeDSNParam(&buf, &hasParam, "maxAllowedPacket", strconv.Itoa(cfg.MaxAllowedPacket))
256✔
379
        }
256✔
380

381
        // other params
382
        if cfg.Params != nil {
2,752✔
383
                var params []string
416✔
384
                for param := range cfg.Params {
864✔
385
                        params = append(params, param)
448✔
386
                }
448✔
387
                sort.Strings(params)
416✔
388
                for _, param := range params {
864✔
389
                        writeDSNParam(&buf, &hasParam, param, url.QueryEscape(cfg.Params[param]))
448✔
390
                }
448✔
391
        }
392

393
        return buf.String()
2,336✔
394
}
395

396
// ParseDSN parses the DSN string to a Config
397
func ParseDSN(dsn string) (cfg *Config, err error) {
11,588✔
398
        // New config with some default values
11,588✔
399
        cfg = NewConfig()
11,588✔
400

11,588✔
401
        // [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
11,588✔
402
        // Find the last '/' (since the password or the net addr might contain a '/')
11,588✔
403
        foundSlash := false
11,588✔
404
        for i := len(dsn) - 1; i >= 0; i-- {
502,526✔
405
                if dsn[i] == '/' {
502,398✔
406
                        foundSlash = true
11,460✔
407
                        var j, k int
11,460✔
408

11,460✔
409
                        // left part is empty if i <= 0
11,460✔
410
                        if i > 0 {
22,408✔
411
                                // [username[:password]@][protocol[(address)]]
10,948✔
412
                                // Find the last '@' in dsn[:i]
10,948✔
413
                                for j = i; j >= 0; j-- {
234,912✔
414
                                        if dsn[j] == '@' {
233,920✔
415
                                                // username[:password]
9,956✔
416
                                                // Find the first ':' in dsn[:j]
9,956✔
417
                                                for k = 0; k < j; k++ { // We cannot use k = range j here, because we use dsn[:k] below
75,744✔
418
                                                        if dsn[k] == ':' {
75,456✔
419
                                                                cfg.Passwd = dsn[k+1 : j]
9,668✔
420
                                                                break
9,668✔
421
                                                        }
422
                                                }
423
                                                cfg.User = dsn[:k]
9,956✔
424

9,956✔
425
                                                break
9,956✔
426
                                        }
427
                                }
428

429
                                // [protocol[(address)]]
430
                                // Find the first '(' in dsn[j+1:i]
431
                                for k = j + 1; k < i; k++ {
56,724✔
432
                                        if dsn[k] == '(' {
56,084✔
433
                                                // dsn[i-1] must be == ')' if an address is specified
10,308✔
434
                                                if dsn[i-1] != ')' {
10,500✔
435
                                                        if strings.ContainsRune(dsn[k+1:i], ')') {
256✔
436
                                                                return nil, errInvalidDSNUnescaped
64✔
437
                                                        }
64✔
438
                                                        return nil, errInvalidDSNAddr
128✔
439
                                                }
440
                                                cfg.Addr = dsn[k+1 : i-1]
10,116✔
441
                                                break
10,116✔
442
                                        }
443
                                }
444
                                cfg.Net = dsn[j+1 : k]
10,756✔
445
                        }
446

447
                        // dbname[?param1=value1&...&paramN=valueN]
448
                        // Find the first '?' in dsn[i+1:]
449
                        for j = i + 1; j < len(dsn); j++ {
85,344✔
450
                                if dsn[j] == '?' {
84,096✔
451
                                        if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
10,052✔
452
                                                return
32✔
453
                                        }
32✔
454
                                        break
9,988✔
455
                                }
456
                        }
457

458
                        dbname := dsn[i+1 : j]
11,236✔
459
                        if cfg.DBName, err = url.PathUnescape(dbname); err != nil {
11,236✔
460
                                return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
×
461
                        }
×
462

463
                        break
11,236✔
464
                }
465
        }
466

467
        if !foundSlash && len(dsn) > 0 {
11,396✔
468
                return nil, errInvalidDSNNoSlash
32✔
469
        }
32✔
470

471
        if err = cfg.normalize(); err != nil {
11,492✔
472
                return nil, err
160✔
473
        }
160✔
474
        return
11,172✔
475
}
476

477
// parseDSNParams parses the DSN "query string"
478
// Values must be url.QueryEscape'ed
479
func parseDSNParams(cfg *Config, params string) (err error) {
10,020✔
480
        for _, v := range strings.Split(params, "&") {
33,162✔
481
                key, value, found := strings.Cut(v, "=")
23,142✔
482
                if !found {
23,142✔
483
                        continue
×
484
                }
485

486
                // cfg params
487
                switch key {
23,142✔
488
                // Disable INFILE allowlist / enable all files
489
                case "allowAllFiles":
160✔
490
                        var isBool bool
160✔
491
                        cfg.AllowAllFiles, isBool = readBool(value)
160✔
492
                        if !isBool {
160✔
493
                                return errors.New("invalid bool value: " + value)
×
494
                        }
×
495

496
                // Use cleartext authentication mode (MySQL 5.5.10+)
497
                case "allowCleartextPasswords":
160✔
498
                        var isBool bool
160✔
499
                        cfg.AllowCleartextPasswords, isBool = readBool(value)
160✔
500
                        if !isBool {
160✔
501
                                return errors.New("invalid bool value: " + value)
×
502
                        }
×
503

504
                // Allow fallback to unencrypted connection if server does not support TLS
505
                case "allowFallbackToPlaintext":
192✔
506
                        var isBool bool
192✔
507
                        cfg.AllowFallbackToPlaintext, isBool = readBool(value)
192✔
508
                        if !isBool {
224✔
509
                                return errors.New("invalid bool value: " + value)
32✔
510
                        }
32✔
511

512
                // Use native password authentication
513
                case "allowNativePasswords":
160✔
514
                        var isBool bool
160✔
515
                        cfg.AllowNativePasswords, isBool = readBool(value)
160✔
516
                        if !isBool {
160✔
517
                                return errors.New("invalid bool value: " + value)
×
518
                        }
×
519

520
                // Use old authentication mode (pre MySQL 4.1)
521
                case "allowOldPasswords":
160✔
522
                        var isBool bool
160✔
523
                        cfg.AllowOldPasswords, isBool = readBool(value)
160✔
524
                        if !isBool {
160✔
525
                                return errors.New("invalid bool value: " + value)
×
526
                        }
×
527

528
                // Check connections for Liveness before using them
529
                case "checkConnLiveness":
160✔
530
                        var isBool bool
160✔
531
                        cfg.CheckConnLiveness, isBool = readBool(value)
160✔
532
                        if !isBool {
160✔
533
                                return errors.New("invalid bool value: " + value)
×
534
                        }
×
535

536
                // Switch "rowsAffected" mode
537
                case "clientFoundRows":
224✔
538
                        var isBool bool
224✔
539
                        cfg.ClientFoundRows, isBool = readBool(value)
224✔
540
                        if !isBool {
224✔
541
                                return errors.New("invalid bool value: " + value)
×
542
                        }
×
543

544
                // charset
545
                case "charset":
960✔
546
                        cfg.charsets = strings.Split(value, ",")
960✔
547

548
                // Collation
549
                case "collation":
896✔
550
                        cfg.Collation = value
896✔
551

552
                case "columnsWithAlias":
320✔
553
                        var isBool bool
320✔
554
                        cfg.ColumnsWithAlias, isBool = readBool(value)
320✔
555
                        if !isBool {
320✔
556
                                return errors.New("invalid bool value: " + value)
×
557
                        }
×
558

559
                // Compression
560
                case "compress":
1,152✔
561
                        var isBool bool
1,152✔
562
                        cfg.compress, isBool = readBool(value)
1,152✔
563
                        if !isBool {
1,152✔
564
                                return errors.New("invalid bool value: " + value)
×
565
                        }
×
566

567
                // Enable client side placeholder substitution
568
                case "interpolateParams":
3,618✔
569
                        var isBool bool
3,618✔
570
                        cfg.InterpolateParams, isBool = readBool(value)
3,618✔
571
                        if !isBool {
3,618✔
572
                                return errors.New("invalid bool value: " + value)
×
573
                        }
×
574

575
                // Time Location
576
                case "loc":
832✔
577
                        if value, err = url.QueryUnescape(value); err != nil {
832✔
578
                                return
×
579
                        }
×
580
                        cfg.Loc, err = time.LoadLocation(value)
832✔
581
                        if err != nil {
832✔
582
                                return
×
583
                        }
×
584

585
                // multiple statements in one query
586
                case "multiStatements":
576✔
587
                        var isBool bool
576✔
588
                        cfg.MultiStatements, isBool = readBool(value)
576✔
589
                        if !isBool {
576✔
590
                                return errors.New("invalid bool value: " + value)
×
591
                        }
×
592

593
                // time.Time parsing
594
                case "parseTime":
1,216✔
595
                        var isBool bool
1,216✔
596
                        cfg.ParseTime, isBool = readBool(value)
1,216✔
597
                        if !isBool {
1,216✔
598
                                return errors.New("invalid bool value: " + value)
×
599
                        }
×
600

601
                // time.Time truncation
602
                case "timeTruncate":
160✔
603
                        cfg.timeTruncate, err = time.ParseDuration(value)
160✔
604
                        if err != nil {
160✔
605
                                return fmt.Errorf("invalid timeTruncate value: %v, error: %w", value, err)
×
606
                        }
×
607

608
                // I/O read Timeout
609
                case "readTimeout":
160✔
610
                        cfg.ReadTimeout, err = time.ParseDuration(value)
160✔
611
                        if err != nil {
160✔
612
                                return
×
613
                        }
×
614

615
                // Reject read-only connections
616
                case "rejectReadOnly":
288✔
617
                        var isBool bool
288✔
618
                        cfg.RejectReadOnly, isBool = readBool(value)
288✔
619
                        if !isBool {
288✔
620
                                return errors.New("invalid bool value: " + value)
×
621
                        }
×
622

623
                // Server public key
624
                case "serverPubKey":
128✔
625
                        name, err := url.QueryUnescape(value)
128✔
626
                        if err != nil {
128✔
627
                                return fmt.Errorf("invalid value for server pub key name: %v", err)
×
628
                        }
×
629
                        cfg.ServerPubKey = name
128✔
630

631
                // Strict mode
632
                case "strict":
×
633
                        panic("strict mode has been removed. See https://github.com/go-sql-driver/mysql/wiki/strict-mode")
×
634

635
                // Dial Timeout
636
                case "timeout":
8,260✔
637
                        cfg.Timeout, err = time.ParseDuration(value)
8,260✔
638
                        if err != nil {
8,260✔
639
                                return
×
640
                        }
×
641

642
                // TLS-Encryption
643
                case "tls":
1,152✔
644
                        boolValue, isBool := readBool(value)
1,152✔
645
                        if isBool {
1,536✔
646
                                if boolValue {
608✔
647
                                        cfg.TLSConfig = "true"
224✔
648
                                } else {
384✔
649
                                        cfg.TLSConfig = "false"
160✔
650
                                }
160✔
651
                        } else if vl := strings.ToLower(value); vl == "skip-verify" || vl == "preferred" {
1,280✔
652
                                cfg.TLSConfig = vl
512✔
653
                        } else {
768✔
654
                                name, err := url.QueryUnescape(value)
256✔
655
                                if err != nil {
256✔
656
                                        return fmt.Errorf("invalid value for TLS config name: %v", err)
×
657
                                }
×
658
                                cfg.TLSConfig = name
256✔
659
                        }
660

661
                // I/O write Timeout
662
                case "writeTimeout":
160✔
663
                        cfg.WriteTimeout, err = time.ParseDuration(value)
160✔
664
                        if err != nil {
160✔
665
                                return
×
666
                        }
×
667
                case "maxAllowedPacket":
448✔
668
                        cfg.MaxAllowedPacket, err = strconv.Atoi(value)
448✔
669
                        if err != nil {
448✔
670
                                return
×
671
                        }
×
672

673
                // Connection attributes
674
                case "connectionAttributes":
544✔
675
                        connectionAttributes, err := url.QueryUnescape(value)
544✔
676
                        if err != nil {
544✔
677
                                return fmt.Errorf("invalid connectionAttributes value: %v", err)
×
678
                        }
×
679
                        cfg.ConnectionAttributes = connectionAttributes
544✔
680

681
                default:
1,056✔
682
                        // lazy init
1,056✔
683
                        if cfg.Params == nil {
2,112✔
684
                                cfg.Params = make(map[string]string)
1,056✔
685
                        }
1,056✔
686

687
                        if cfg.Params[key], err = url.QueryUnescape(value); err != nil {
1,056✔
688
                                return
×
689
                        }
×
690
                }
691
        }
692

693
        return
9,988✔
694
}
695

696
func ensureHavePort(addr string) string {
9,540✔
697
        if _, _, err := net.SplitHostPort(addr); err != nil {
9,764✔
698
                return net.JoinHostPort(addr, "3306")
224✔
699
        }
224✔
700
        return addr
9,316✔
701
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc