Skip to content

Commit 73eacf5

Browse files
committed
Merge branch 'master' of github.com:badoet/mysql
2 parents d512f20 + 957f196 commit 73eacf5

File tree

6 files changed

+137
-14
lines changed

6 files changed

+137
-14
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Stan Putrya <root.vagner at gmail.com>
3838
Xiaobing Jiang <s7v7nislands at gmail.com>
3939
Xiuming Chen <cc at cxm.cc>
4040
Julien Lefevre <julien.lefevr at gmail.com>
41+
Stanley Gunawan <gunawan.stanley at gmail.com>
4142

4243
# Organizations
4344

connection.go

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type config struct {
5050
clientFoundRows bool
5151
columnsWithAlias bool
5252
interpolateParams bool
53+
multiStatements bool
5354
}
5455

5556
// Handles parameters set in DSN after the connection is established

driver_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ type DBTest struct {
7474
db *sql.DB
7575
}
7676

77+
func runTestsWithMultiStatement(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
78+
if !available {
79+
t.Skipf("MySQL-Server not running on %s", netAddr)
80+
}
81+
82+
dsn3 := dsn + "&multiStatements=true"
83+
var db3 *sql.DB
84+
if _, err := parseDSN(dsn3); err != errInvalidDSNUnsafeCollation {
85+
db3, err = sql.Open("mysql", dsn3)
86+
if err != nil {
87+
t.Fatalf("Error connecting: %s", err.Error())
88+
}
89+
defer db3.Close()
90+
}
91+
92+
dbt3 := &DBTest{t, db3}
93+
for _, test := range tests {
94+
test(dbt3)
95+
dbt3.db.Exec("DROP TABLE IF EXISTS test")
96+
}
97+
}
98+
7799
func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
78100
if !available {
79101
t.Skipf("MySQL-Server not running on %s", netAddr)
@@ -97,15 +119,30 @@ func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) {
97119
defer db2.Close()
98120
}
99121

122+
dsn3 := dsn + "&multiStatements=true"
123+
var db3 *sql.DB
124+
if _, err := parseDSN(dsn3); err != errInvalidDSNUnsafeCollation {
125+
db3, err = sql.Open("mysql", dsn3)
126+
if err != nil {
127+
t.Fatalf("Error connecting: %s", err.Error())
128+
}
129+
defer db3.Close()
130+
}
131+
100132
dbt := &DBTest{t, db}
101133
dbt2 := &DBTest{t, db2}
134+
dbt3 := &DBTest{t, db3}
102135
for _, test := range tests {
103136
test(dbt)
104137
dbt.db.Exec("DROP TABLE IF EXISTS test")
105138
if db2 != nil {
106139
test(dbt2)
107140
dbt2.db.Exec("DROP TABLE IF EXISTS test")
108141
}
142+
if db3 != nil {
143+
test(dbt3)
144+
dbt3.db.Exec("DROP TABLE IF EXISTS test")
145+
}
109146
}
110147
}
111148

@@ -235,6 +272,50 @@ func TestCRUD(t *testing.T) {
235272
})
236273
}
237274

275+
func TestMultiQuery(t *testing.T) {
276+
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
277+
// Create Table
278+
dbt.mustExec("CREATE TABLE `test` (`id` int(11) NOT NULL, `value` int(11) NOT NULL) ")
279+
280+
// Create Data
281+
res := dbt.mustExec("INSERT INTO test VALUES (1, 1)")
282+
count, err := res.RowsAffected()
283+
if err != nil {
284+
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
285+
}
286+
if count != 1 {
287+
dbt.Fatalf("Expected 1 affected row, got %d", count)
288+
}
289+
290+
// Update
291+
res = dbt.mustExec("UPDATE test SET value = 3 WHERE id = 1; UPDATE test SET value = 4 WHERE id = 1; UPDATE test SET value = 5 WHERE id = 1;")
292+
count, err = res.RowsAffected()
293+
if err != nil {
294+
dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error())
295+
}
296+
if count != 1 {
297+
dbt.Fatalf("Expected 1 affected row, got %d", count)
298+
}
299+
300+
// Read
301+
var out int
302+
rows := dbt.mustQuery("SELECT value FROM test WHERE id=1;")
303+
if rows.Next() {
304+
rows.Scan(&out)
305+
if 5 != out {
306+
dbt.Errorf("5 != %t", out)
307+
}
308+
309+
if rows.Next() {
310+
dbt.Error("unexpected data")
311+
}
312+
} else {
313+
dbt.Error("no data")
314+
}
315+
316+
})
317+
}
318+
238319
func TestInt(t *testing.T) {
239320
runTests(t, dsn, func(dbt *DBTest) {
240321
types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"}

packets.go

+33-1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
230230
clientFlags |= clientSSL
231231
}
232232

233+
if mc.cfg.multiStatements {
234+
clientFlags |= clientMultiStatements
235+
}
236+
233237
// User Password
234238
scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.passwd))
235239

@@ -514,6 +518,32 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
514518
}
515519
}
516520

521+
func readStatus(b []byte) statusFlag {
522+
return statusFlag(b[0]) | statusFlag(b[1])<<8
523+
}
524+
525+
func (mc *mysqlConn) discardMoreResultsIfExists() error {
526+
for mc.status&statusMoreResultsExists != 0 {
527+
resLen, err := mc.readResultSetHeaderPacket()
528+
if err != nil {
529+
return err
530+
}
531+
if resLen > 0 {
532+
// columns
533+
if err := mc.readUntilEOF(); err != nil {
534+
return err
535+
}
536+
// rows
537+
if err := mc.readUntilEOF(); err != nil {
538+
return err
539+
}
540+
} else {
541+
mc.status &^= statusMoreResultsExists
542+
}
543+
}
544+
return nil
545+
}
546+
517547
// Ok Packet
518548
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
519549
func (mc *mysqlConn) handleOkPacket(data []byte) error {
@@ -528,7 +558,9 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
528558
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
529559

530560
// server_status [2 bytes]
531-
mc.status = statusFlag(data[1+n+m]) | statusFlag(data[1+n+m+1])<<8
561+
// mc.status = statusFlag(data[1+n+m]) | statusFlag(data[1+n+m+1])<<8
562+
mc.status = readStatus(data[1+n+m : 1+n+m+2])
563+
mc.discardMoreResultsIfExists()
532564

533565
// warning count [2 bytes]
534566
if !mc.strict {

utils.go

+7
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,13 @@ func parseDSNParams(cfg *config, params string) (err error) {
284284
}
285285
}
286286

287+
case "multiStatements":
288+
var isBool bool
289+
cfg.multiStatements, isBool = readBool(value)
290+
if !isBool {
291+
return fmt.Errorf("Invalid Bool value: %s", value)
292+
}
293+
287294
default:
288295
// lazy init
289296
if cfg.params == nil {

utils_test.go

+14-13
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,20 @@ 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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
26-
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false}", time.UTC},
27-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
28-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
29-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
30-
{"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 allowCleartextPasswords:false clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC},
31-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.Local},
32-
{"/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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams: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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams: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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
35-
{"", "&{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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
36-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
37-
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams: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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
26+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false multiStatements:false}", time.UTC},
27+
{"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] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false multiStatements:true}", time.UTC},
28+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
29+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
30+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
31+
{"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 allowCleartextPasswords:false clientFoundRows:true columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
32+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.Local},
33+
{"/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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements: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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
35+
{"/", "&{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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
36+
{"", "&{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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
37+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
38+
{"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 allowCleartextPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false multiStatements:false}", time.UTC},
3839
}
3940

4041
func TestDSNParser(t *testing.T) {

0 commit comments

Comments
 (0)