Skip to content

Commit b7c2c47

Browse files
committed
Add substitutePlaceholder option to DSN
1 parent cac6129 commit b7c2c47

File tree

4 files changed

+49
-25
lines changed

4 files changed

+49
-25
lines changed

connection.go

+20-13
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,20 @@ type mysqlConn struct {
3434
}
3535

3636
type config struct {
37-
user string
38-
passwd string
39-
net string
40-
addr string
41-
dbname string
42-
params map[string]string
43-
loc *time.Location
44-
tls *tls.Config
45-
timeout time.Duration
46-
collation uint8
47-
allowAllFiles bool
48-
allowOldPasswords bool
49-
clientFoundRows bool
37+
user string
38+
passwd string
39+
net string
40+
addr string
41+
dbname string
42+
params map[string]string
43+
loc *time.Location
44+
tls *tls.Config
45+
timeout time.Duration
46+
collation uint8
47+
allowAllFiles bool
48+
allowOldPasswords bool
49+
clientFoundRows bool
50+
substitutePlaceholder bool
5051
}
5152

5253
// Handles parameters set in DSN after the connection is established
@@ -281,6 +282,9 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
281282
return nil, driver.ErrBadConn
282283
}
283284
if len(args) != 0 {
285+
if !mc.cfg.substitutePlaceholder {
286+
return nil, driver.ErrSkip
287+
}
284288
// try client-side prepare to reduce roundtrip
285289
prepared, err := mc.buildQuery(query, args)
286290
if err != nil {
@@ -329,6 +333,9 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
329333
return nil, driver.ErrBadConn
330334
}
331335
if len(args) != 0 {
336+
if !mc.cfg.substitutePlaceholder {
337+
return nil, driver.ErrSkip
338+
}
332339
// try client-side prepare to reduce roundtrip
333340
prepared, err := mc.buildQuery(query, args)
334341
if err != nil {

driver_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,19 @@ func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
8787

8888
db.Exec("DROP TABLE IF EXISTS test")
8989

90+
dbp, err := sql.Open("mysql", dsn+"&substitutePlaceholder=true")
91+
if err != nil {
92+
t.Fatalf("Error connecting: %s", err.Error())
93+
}
94+
defer dbp.Close()
95+
9096
dbt := &DBTest{t, db}
97+
dbtp := &DBTest{t, dbp}
9198
for _, test := range tests {
9299
test(dbt)
93100
dbt.db.Exec("DROP TABLE IF EXISTS test")
101+
test(dbtp)
102+
dbtp.db.Exec("DROP TABLE IF EXISTS test")
94103
}
95104
}
96105

utils.go

+8
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ func parseDSNParams(cfg *config, params string) (err error) {
180180
// cfg params
181181
switch value := param[1]; param[0] {
182182

183+
// Enable client side placeholder substitution
184+
case "substitutePlaceholder":
185+
var isBool bool
186+
cfg.substitutePlaceholder, isBool = readBool(value)
187+
if !isBool {
188+
return fmt.Errorf("Invalid Bool value: %s", value)
189+
}
190+
183191
// Disable INFILE whitelist / enable all files
184192
case "allowAllFiles":
185193
var isBool bool

utils_test.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,18 @@ var testDSNs = []struct {
2222
out string
2323
loc *time.Location
2424
}{
25-
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
26-
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
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] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
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] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
29-
{"user:password@/dbname?loc=UTC&timeout=30s&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[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
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[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
31-
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
32-
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
33-
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
34-
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
35-
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
36-
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
25+
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
26+
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
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] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
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] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
29+
{"user:password@/dbname?loc=UTC&timeout=30s&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[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true substitutePlaceholder:false}", time.UTC},
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[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.Local},
31+
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
32+
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
33+
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
34+
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
35+
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
36+
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false substitutePlaceholder:false}", time.UTC},
3737
}
3838

3939
func TestDSNParser(t *testing.T) {

0 commit comments

Comments
 (0)