Skip to content

Commit c76053d

Browse files
Merge branch 'master' into tlsconfig
2 parents 808c9b4 + d2a8175 commit c76053d

14 files changed

+314
-68
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
Icon?
77
ehthumbs.db
88
Thumbs.db
9+
.idea

.travis.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
sudo: false
22
language: go
33
go:
4-
- 1.2
5-
- 1.3
6-
- 1.4
74
- 1.5
85
- 1.6
96
- 1.7

AUTHORS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# Individual Persons
1313

1414
Aaron Hopkins <go-sql-driver at die.net>
15+
Achille Roussel <achille.roussel at gmail.com>
1516
Arne Hormann <arnehormann at gmail.com>
1617
Asta Xie <xiemengjun at gmail.com>
1718
Bulat Gaifullin <gaifullinbf at gmail.com>
@@ -45,6 +46,7 @@ Lion Yang <lion at aosc.xyz>
4546
Luca Looz <luca.looz92 at gmail.com>
4647
Lucas Liu <extrafliu at gmail.com>
4748
Luke Scott <luke at webconnex.com>
49+
Maciej Zimnoch <[email protected]>
4850
Michael Woolnough <michael.woolnough at gmail.com>
4951
Nicola Peduzzi <thenikso at gmail.com>
5052
Olivier Mengué <dolmen at cpan.org>

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
3939
* Optional placeholder interpolation
4040

4141
## Requirements
42-
* Go 1.2 or higher
42+
* Go 1.5 or higher
4343
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
4444

4545
---------------------------------------
@@ -279,7 +279,7 @@ Default: false
279279

280280
`rejectreadOnly=true` causes the driver to reject read-only connections. This
281281
is for a possible race condition during an automatic failover, where the mysql
282-
client gets connected to a read-only replica after the failover.
282+
client gets connected to a read-only replica after the failover.
283283

284284
Note that this should be a fairly rare case, as an automatic failover normally
285285
happens when the primary is down, and the race condition shouldn't happen

connection.go

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@ import (
1414
"net"
1515
"strconv"
1616
"strings"
17-
"sync"
18-
"sync/atomic"
1917
"time"
2018
)
2119

22-
// a copy of context.Context for Go 1.7 and later.
20+
// a copy of context.Context for Go 1.7 and earlier
2321
type mysqlContext interface {
2422
Done() <-chan struct{}
2523
Err() error
2624

27-
// They are defined in context.Context, but go-mysql-driver does not use them.
25+
// defined in context.Context, but not used in this driver:
2826
// Deadline() (deadline time.Time, ok bool)
2927
// Value(key interface{}) interface{}
3028
}
@@ -44,18 +42,13 @@ type mysqlConn struct {
4442
parseTime bool
4543
strict bool
4644

47-
// for context support (From Go 1.8)
45+
// for context support (Go 1.8+)
4846
watching bool
4947
watcher chan<- mysqlContext
5048
closech chan struct{}
5149
finished chan<- struct{}
52-
53-
// set non-zero when conn is closed, before closech is closed.
54-
// accessed atomically.
55-
closed int32
56-
57-
mu sync.Mutex // guards following fields
58-
canceledErr error // set non-nil if conn is canceled
50+
canceled atomicError // set non-nil if conn is canceled
51+
closed atomicBool // set when conn is closed, before closech is closed
5952
}
6053

6154
// Handles parameters set in DSN after the connection is established
@@ -89,7 +82,7 @@ func (mc *mysqlConn) handleParams() (err error) {
8982
}
9083

9184
func (mc *mysqlConn) Begin() (driver.Tx, error) {
92-
if mc.isBroken() {
85+
if mc.closed.IsSet() {
9386
errLog.Print(ErrInvalidConn)
9487
return nil, driver.ErrBadConn
9588
}
@@ -103,7 +96,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
10396

10497
func (mc *mysqlConn) Close() (err error) {
10598
// Makes Close idempotent
106-
if !mc.isBroken() {
99+
if !mc.closed.IsSet() {
107100
err = mc.writeCommandPacket(comQuit)
108101
}
109102

@@ -117,7 +110,7 @@ func (mc *mysqlConn) Close() (err error) {
117110
// is called before auth or on auth failure because MySQL will have already
118111
// closed the network connection.
119112
func (mc *mysqlConn) cleanup() {
120-
if atomic.SwapInt32(&mc.closed, 1) != 0 {
113+
if !mc.closed.TrySet(true) {
121114
return
122115
}
123116

@@ -131,13 +124,9 @@ func (mc *mysqlConn) cleanup() {
131124
}
132125
}
133126

134-
func (mc *mysqlConn) isBroken() bool {
135-
return atomic.LoadInt32(&mc.closed) != 0
136-
}
137-
138127
func (mc *mysqlConn) error() error {
139-
if mc.isBroken() {
140-
if err := mc.canceled(); err != nil {
128+
if mc.closed.IsSet() {
129+
if err := mc.canceled.Value(); err != nil {
141130
return err
142131
}
143132
return ErrInvalidConn
@@ -146,7 +135,7 @@ func (mc *mysqlConn) error() error {
146135
}
147136

148137
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
149-
if mc.isBroken() {
138+
if mc.closed.IsSet() {
150139
errLog.Print(ErrInvalidConn)
151140
return nil, driver.ErrBadConn
152141
}
@@ -300,7 +289,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
300289
}
301290

302291
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
303-
if mc.isBroken() {
292+
if mc.closed.IsSet() {
304293
errLog.Print(ErrInvalidConn)
305294
return nil, driver.ErrBadConn
306295
}
@@ -361,7 +350,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
361350
}
362351

363352
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
364-
if mc.isBroken() {
353+
if mc.closed.IsSet() {
365354
errLog.Print(ErrInvalidConn)
366355
return nil, driver.ErrBadConn
367356
}
@@ -436,19 +425,10 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
436425

437426
// finish is called when the query has canceled.
438427
func (mc *mysqlConn) cancel(err error) {
439-
mc.mu.Lock()
440-
mc.canceledErr = err
441-
mc.mu.Unlock()
428+
mc.canceled.Set(err)
442429
mc.cleanup()
443430
}
444431

445-
// canceled returns non-nil if the connection was closed due to context cancelation.
446-
func (mc *mysqlConn) canceled() error {
447-
mc.mu.Lock()
448-
defer mc.mu.Unlock()
449-
return mc.canceledErr
450-
}
451-
452432
// finish is called when the query has succeeded.
453433
func (mc *mysqlConn) finish() {
454434
if !mc.watching || mc.finished == nil {

connection_go18.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
// Ping implements driver.Pinger interface
2121
func (mc *mysqlConn) Ping(ctx context.Context) error {
22-
if mc.isBroken() {
22+
if mc.closed.IsSet() {
2323
errLog.Print(ErrInvalidConn)
2424
return driver.ErrBadConn
2525
}
@@ -41,10 +41,6 @@ func (mc *mysqlConn) Ping(ctx context.Context) error {
4141

4242
// BeginTx implements driver.ConnBeginTx interface
4343
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
44-
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
45-
// TODO: support isolation levels
46-
return nil, errors.New("mysql: isolation levels not supported")
47-
}
4844
if opts.ReadOnly {
4945
// TODO: support read-only transactions
5046
return nil, errors.New("mysql: read-only transactions not supported")
@@ -54,19 +50,20 @@ func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver
5450
return nil, err
5551
}
5652

57-
tx, err := mc.Begin()
58-
mc.finish()
59-
if err != nil {
60-
return nil, err
61-
}
53+
defer mc.finish()
6254

63-
select {
64-
default:
65-
case <-ctx.Done():
66-
tx.Rollback()
67-
return nil, ctx.Err()
55+
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
56+
level, err := mapIsolationLevel(opts.Isolation)
57+
if err != nil {
58+
return nil, err
59+
}
60+
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
61+
if err != nil {
62+
return nil, err
63+
}
6864
}
69-
return tx, err
65+
66+
return mc.Begin()
7067
}
7168

7269
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {

driver_go18_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,55 @@ func TestContextCancelBegin(t *testing.T) {
468468
}
469469
})
470470
}
471+
472+
func TestContextBeginIsolationLevel(t *testing.T) {
473+
runTests(t, dsn, func(dbt *DBTest) {
474+
dbt.mustExec("CREATE TABLE test (v INTEGER)")
475+
ctx, cancel := context.WithCancel(context.Background())
476+
defer cancel()
477+
478+
tx1, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
479+
Isolation: sql.LevelRepeatableRead,
480+
})
481+
if err != nil {
482+
dbt.Fatal(err)
483+
}
484+
485+
tx2, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
486+
Isolation: sql.LevelReadCommitted,
487+
})
488+
if err != nil {
489+
dbt.Fatal(err)
490+
}
491+
492+
_, err = tx1.ExecContext(ctx, "INSERT INTO test VALUES (1)")
493+
if err != nil {
494+
dbt.Fatal(err)
495+
}
496+
497+
var v int
498+
row := tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
499+
if err := row.Scan(&v); err != nil {
500+
dbt.Fatal(err)
501+
}
502+
// Because writer transaction wasn't commited yet, it should be available
503+
if v != 0 {
504+
dbt.Errorf("expected val to be 0, got %d", v)
505+
}
506+
507+
err = tx1.Commit()
508+
if err != nil {
509+
dbt.Fatal(err)
510+
}
511+
512+
row = tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
513+
if err := row.Scan(&v); err != nil {
514+
dbt.Fatal(err)
515+
}
516+
// Data written by writer transaction is already commited, it should be selectable
517+
if v != 1 {
518+
dbt.Errorf("expected val to be 1, got %d", v)
519+
}
520+
tx2.Commit()
521+
})
522+
}

packets.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
3030
// read packet header
3131
data, err := mc.buf.readNext(4)
3232
if err != nil {
33-
if cerr := mc.canceled(); cerr != nil {
33+
if cerr := mc.canceled.Value(); cerr != nil {
3434
return nil, cerr
3535
}
3636
errLog.Print(err)
@@ -66,7 +66,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
6666
// read packet body [pktLen bytes]
6767
data, err = mc.buf.readNext(pktLen)
6868
if err != nil {
69-
if cerr := mc.canceled(); cerr != nil {
69+
if cerr := mc.canceled.Value(); cerr != nil {
7070
return nil, cerr
7171
}
7272
errLog.Print(err)
@@ -134,7 +134,7 @@ func (mc *mysqlConn) writePacket(data []byte) error {
134134
mc.cleanup()
135135
errLog.Print(ErrMalformPkt)
136136
} else {
137-
if cerr := mc.canceled(); cerr != nil {
137+
if cerr := mc.canceled.Value(); cerr != nil {
138138
return cerr
139139
}
140140
mc.cleanup()
@@ -1078,17 +1078,19 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
10781078
paramTypes[i+i] = fieldTypeString
10791079
paramTypes[i+i+1] = 0x00
10801080

1081-
var val []byte
1081+
var a [64]byte
1082+
var b = a[:0]
1083+
10821084
if v.IsZero() {
1083-
val = []byte("0000-00-00")
1085+
b = append(b, "0000-00-00"...)
10841086
} else {
1085-
val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
1087+
b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
10861088
}
10871089

10881090
paramValues = appendLengthEncodedInteger(paramValues,
1089-
uint64(len(val)),
1091+
uint64(len(b)),
10901092
)
1091-
paramValues = append(paramValues, val...)
1093+
paramValues = append(paramValues, b...)
10921094

10931095
default:
10941096
return fmt.Errorf("can not convert type: %T", arg)

statement.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type mysqlStmt struct {
2323
}
2424

2525
func (stmt *mysqlStmt) Close() error {
26-
if stmt.mc == nil || stmt.mc.isBroken() {
26+
if stmt.mc == nil || stmt.mc.closed.IsSet() {
2727
// driver.Stmt.Close can be called more than once, thus this function
2828
// has to be idempotent.
2929
// See also Issue #450 and golang/go#16019.
@@ -45,7 +45,7 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
4545
}
4646

4747
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
48-
if stmt.mc.isBroken() {
48+
if stmt.mc.closed.IsSet() {
4949
errLog.Print(ErrInvalidConn)
5050
return nil, driver.ErrBadConn
5151
}
@@ -93,7 +93,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
9393
}
9494

9595
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
96-
if stmt.mc.isBroken() {
96+
if stmt.mc.closed.IsSet() {
9797
errLog.Print(ErrInvalidConn)
9898
return nil, driver.ErrBadConn
9999
}

transaction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type mysqlTx struct {
1313
}
1414

1515
func (tx *mysqlTx) Commit() (err error) {
16-
if tx.mc == nil || tx.mc.isBroken() {
16+
if tx.mc == nil || tx.mc.closed.IsSet() {
1717
return ErrInvalidConn
1818
}
1919
err = tx.mc.exec("COMMIT")
@@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
2222
}
2323

2424
func (tx *mysqlTx) Rollback() (err error) {
25-
if tx.mc == nil || tx.mc.isBroken() {
25+
if tx.mc == nil || tx.mc.closed.IsSet() {
2626
return ErrInvalidConn
2727
}
2828
err = tx.mc.exec("ROLLBACK")

0 commit comments

Comments
 (0)