Skip to content

Commit 78b7a05

Browse files
committed
Merge pull request #1 from go-sql-driver/master
pull in most recent chages
2 parents ceeeaea + 0792be4 commit 78b7a05

18 files changed

+512
-169
lines changed

AUTHORS

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This is the official list of Go-MySQL-Driver authors for copyright purposes.
2+
3+
# If you are submitting a patch, please add your name or the name of the
4+
# organization which holds the copyright to this list in alphabetical order.
5+
6+
# Names should be added to this file as
7+
# Name <email address>
8+
# The email address is not required for organizations.
9+
# Please keep the list sorted.
10+
11+
12+
# Individual Persons
13+
14+
Arne Hormann <arnehormann at gmail.com>
15+
Hanno Braun <mail at hannobraun.com>
16+
James Harr <james.harr at gmail.com>
17+
Julien Schmidt <go-sql-driver at julienschmidt.com>
18+
Lucas Liu <extrafliu at gmail.com>
19+
Luke Scott <luke at webconnex.com>
20+
Michael Woolnough <michael.woolnough at gmail.com>
21+
Nicola Peduzzi <thenikso at gmail.com>
22+
Xiaobing Jiang <s7v7nislands at gmail.com>
23+
24+
25+
# Organizations
26+
27+
Google Inc.

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## 1.1 (pending)
2+
3+
Changes:
4+
5+
- Go-MySQL-Driver now requires Go 1.1
6+
- Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore
7+
- Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors
8+
- New Logo
9+
- Changed the copyright header to include all contributors
10+
- Optimized the read buffer
11+
- Improved the LOAD INFILE documentation
12+
- The driver struct is now exported to make the driver directly accessible
13+
- Refactored the driver tests
14+
- Added more benchmarks and moved all to a separate file
15+
- Other small refactoring
16+
17+
New Features:
18+
19+
- Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure
20+
- Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs
21+
- Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used
22+
23+
Bugfixes:
24+
25+
- Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
26+
- Convert to DB timezone when inserting time.Time
27+
- Splitted packets (more than 16MB) are now merged correctly
28+
29+
30+
## 1.0 (2013-05-14)
31+
32+
Initial Release

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ For Unix domain sockets the address is the absolute path to the MySQL-Server-soc
107107

108108
Possible Parameters are:
109109
* `allowAllFiles`: `allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
110+
* `allowOldPasswords`: `allowAllFiles=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords).
110111
* `charset`: Sets the charset used for client-server interaction ("SET NAMES `value`"). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
111112
* `clientFoundRows`: `clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
112113
* `loc`: Sets the location for time.Time values (when using `parseTime=true`). The default is `UTC`. *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.

benchmark_test.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
2+
//
3+
// Copyright 2013 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+
"database/sql"
14+
"strings"
15+
"sync"
16+
"sync/atomic"
17+
"testing"
18+
)
19+
20+
type TB testing.B
21+
22+
func (tb *TB) check(err error) {
23+
if err != nil {
24+
tb.Fatal(err)
25+
}
26+
}
27+
28+
func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
29+
tb.check(err)
30+
return db
31+
}
32+
33+
func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
34+
tb.check(err)
35+
return rows
36+
}
37+
38+
func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
39+
tb.check(err)
40+
return stmt
41+
}
42+
43+
func initDB(b *testing.B, queries ...string) *sql.DB {
44+
tb := (*TB)(b)
45+
db := tb.checkDB(sql.Open("mysql", dsn))
46+
for _, query := range queries {
47+
if _, err := db.Exec(query); err != nil {
48+
b.Fatalf("Error on %q: %v", query, err)
49+
}
50+
}
51+
return db
52+
}
53+
54+
// by Brad Fitzpatrick
55+
const concurrencyLevel = 10
56+
57+
func BenchmarkQuery(b *testing.B) {
58+
tb := (*TB)(b)
59+
b.StopTimer()
60+
b.ReportAllocs()
61+
db := initDB(b,
62+
"DROP TABLE IF EXISTS foo",
63+
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
64+
`INSERT INTO foo VALUES (1, "one")`,
65+
`INSERT INTO foo VALUES (2, "two")`,
66+
)
67+
db.SetMaxIdleConns(concurrencyLevel)
68+
defer db.Close()
69+
70+
stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
71+
defer stmt.Close()
72+
b.StartTimer()
73+
74+
remain := int64(b.N)
75+
var wg sync.WaitGroup
76+
wg.Add(concurrencyLevel)
77+
defer wg.Wait()
78+
for i := 0; i < concurrencyLevel; i++ {
79+
go func() {
80+
defer wg.Done()
81+
for {
82+
if atomic.AddInt64(&remain, -1) < 0 {
83+
return
84+
}
85+
var got string
86+
tb.check(stmt.QueryRow(1).Scan(&got))
87+
if got != "one" {
88+
b.Errorf("query = %q; want one", got)
89+
return
90+
}
91+
}
92+
}()
93+
}
94+
}
95+
96+
// data, but no db writes
97+
var roundtripSample []byte
98+
99+
func initRoundtripBenchmarks() ([]byte, int, int) {
100+
if roundtripSample == nil {
101+
roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
102+
}
103+
return roundtripSample, 16, len(roundtripSample)
104+
}
105+
106+
func BenchmarkRoundtripTxt(b *testing.B) {
107+
b.StopTimer()
108+
sample, min, max := initRoundtripBenchmarks()
109+
sampleString := string(sample)
110+
b.ReportAllocs()
111+
tb := (*TB)(b)
112+
db := tb.checkDB(sql.Open("mysql", dsn))
113+
defer db.Close()
114+
b.StartTimer()
115+
var result string
116+
for i := 0; i < b.N; i++ {
117+
length := min + i
118+
if length > max {
119+
length = max
120+
}
121+
test := sampleString[0:length]
122+
rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
123+
if !rows.Next() {
124+
rows.Close()
125+
b.Fatalf("crashed")
126+
}
127+
err := rows.Scan(&result)
128+
if err != nil {
129+
rows.Close()
130+
b.Fatalf("crashed")
131+
}
132+
if result != test {
133+
rows.Close()
134+
b.Errorf("mismatch")
135+
}
136+
rows.Close()
137+
}
138+
}
139+
140+
func BenchmarkRoundtripBin(b *testing.B) {
141+
b.StopTimer()
142+
sample, min, max := initRoundtripBenchmarks()
143+
b.ReportAllocs()
144+
tb := (*TB)(b)
145+
db := tb.checkDB(sql.Open("mysql", dsn))
146+
defer db.Close()
147+
stmt := tb.checkStmt(db.Prepare("SELECT ?"))
148+
defer stmt.Close()
149+
b.StartTimer()
150+
var result sql.RawBytes
151+
for i := 0; i < b.N; i++ {
152+
length := min + i
153+
if length > max {
154+
length = max
155+
}
156+
test := sample[0:length]
157+
rows := tb.checkRows(stmt.Query(test))
158+
if !rows.Next() {
159+
rows.Close()
160+
b.Fatalf("crashed")
161+
}
162+
err := rows.Scan(&result)
163+
if err != nil {
164+
rows.Close()
165+
b.Fatalf("crashed")
166+
}
167+
if !bytes.Equal(result, test) {
168+
rows.Close()
169+
b.Errorf("mismatch")
170+
}
171+
rows.Close()
172+
}
173+
}

buffer.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
22
//
3-
// Copyright 2013 Julien Schmidt. All rights reserved.
4-
// http://www.julienschmidt.com
3+
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
54
//
65
// This Source Code Form is subject to the terms of the Mozilla Public
76
// License, v. 2.0. If a copy of the MPL was not distributed with this file,

connection.go

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
22
//
3-
// Copyright 2012 Julien Schmidt. All rights reserved.
4-
// http://www.julienschmidt.com
3+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
54
//
65
// This Source Code Form is subject to the terms of the Mozilla Public
76
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
@@ -21,7 +20,6 @@ import (
2120
type mysqlConn struct {
2221
cfg *config
2322
flags clientFlag
24-
cipher []byte
2523
netConn net.Conn
2624
buf *buffer
2725
protocol uint8
@@ -35,17 +33,18 @@ type mysqlConn struct {
3533
}
3634

3735
type config struct {
38-
user string
39-
passwd string
40-
net string
41-
addr string
42-
dbname string
43-
params map[string]string
44-
loc *time.Location
45-
timeout time.Duration
46-
tls *tls.Config
47-
allowAllFiles bool
48-
clientFoundRows bool
36+
user string
37+
passwd string
38+
net string
39+
addr string
40+
dbname string
41+
params map[string]string
42+
loc *time.Location
43+
timeout time.Duration
44+
tls *tls.Config
45+
allowAllFiles bool
46+
allowOldPasswords bool
47+
clientFoundRows bool
4948
}
5049

5150
// Handles parameters set in DSN
@@ -109,11 +108,16 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
109108
}
110109

111110
func (mc *mysqlConn) Close() (err error) {
112-
mc.writeCommandPacket(comQuit)
111+
// Makes Close idempotent
112+
if mc.netConn != nil {
113+
mc.writeCommandPacket(comQuit)
114+
mc.netConn.Close()
115+
mc.netConn = nil
116+
}
117+
113118
mc.cfg = nil
114119
mc.buf = nil
115-
mc.netConn.Close()
116-
mc.netConn = nil
120+
117121
return
118122
}
119123

const.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
22
//
3-
// Copyright 2012 Julien Schmidt. All rights reserved.
4-
// http://www.julienschmidt.com
3+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
54
//
65
// This Source Code Form is subject to the terms of the Mozilla Public
76
// License, v. 2.0. If a copy of the MPL was not distributed with this file,

driver.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// Copyright 2012 Julien Schmidt. All rights reserved.
2-
// http://www.julienschmidt.com
1+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
32
//
43
// This Source Code Form is subject to the terms of the Mozilla Public
54
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
@@ -52,26 +51,42 @@ func (d *MySQLDriver) Open(dsn string) (driver.Conn, error) {
5251
mc.buf = newBuffer(mc.netConn)
5352

5453
// Reading Handshake Initialization Packet
55-
err = mc.readInitPacket()
54+
cipher, err := mc.readInitPacket()
5655
if err != nil {
56+
mc.Close()
5757
return nil, err
5858
}
5959

6060
// Send Client Authentication Packet
61-
err = mc.writeAuthPacket()
62-
if err != nil {
61+
if err = mc.writeAuthPacket(cipher); err != nil {
62+
mc.Close()
6363
return nil, err
6464
}
6565

6666
// Read Result Packet
6767
err = mc.readResultOK()
6868
if err != nil {
69-
return nil, err
69+
// Retry with old authentication method, if allowed
70+
if mc.cfg.allowOldPasswords && err == errOldPassword {
71+
if err = mc.writeOldAuthPacket(cipher); err != nil {
72+
mc.Close()
73+
return nil, err
74+
}
75+
if err = mc.readResultOK(); err != nil {
76+
mc.Close()
77+
return nil, err
78+
}
79+
} else {
80+
mc.Close()
81+
return nil, err
82+
}
83+
7084
}
7185

7286
// Get max allowed packet size
7387
maxap, err := mc.getSystemVar("max_allowed_packet")
7488
if err != nil {
89+
mc.Close()
7590
return nil, err
7691
}
7792
mc.maxPacketAllowed = stringToInt(maxap) - 1
@@ -82,10 +97,11 @@ func (d *MySQLDriver) Open(dsn string) (driver.Conn, error) {
8297
// Handle DSN Params
8398
err = mc.handleParams()
8499
if err != nil {
100+
mc.Close()
85101
return nil, err
86102
}
87103

88-
return mc, err
104+
return mc, nil
89105
}
90106

91107
func init() {

0 commit comments

Comments
 (0)