Skip to content

Commit 94e3ba7

Browse files
oscarzhaoEvan Shaw
authored andcommitted
Add context support
For backward compatibility with Go versions < 1.8, the file ctx_backport.go contains a redefinition of context.Context as well as a few other things. The result is that the vast majority of the code can be shared between Go versions.
1 parent ae5a66d commit 94e3ba7

12 files changed

+270
-46
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,11 @@ Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAM
418418

419419
See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
420420

421+
## Context Support
422+
Since go1.8, context is introduced to `database/sql` for better control on timeout and cancellation.
423+
New interfaces such as `driver.QueryerContext`, `driver.ExecerContext` are introduced. See more details on [context support to database/sql package](https://golang.org/doc/go1.8#database_sql, "sql").
424+
425+
In Go-MySQL-Driver, we implemented these interfaces for structs `mysqlConn`, `mysqlStmt` and `mysqlTx`.
421426

422427
## Testing / Development
423428
To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.

connection.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func (mc *mysqlConn) handleParams() (err error) {
4242
charsets := strings.Split(val, ",")
4343
for i := range charsets {
4444
// ignore errors here - a charset may not exist
45-
err = mc.exec("SET NAMES " + charsets[i])
45+
err = mc.exec(backgroundCtx(), "SET NAMES "+charsets[i])
4646
if err == nil {
4747
break
4848
}
@@ -53,7 +53,7 @@ func (mc *mysqlConn) handleParams() (err error) {
5353

5454
// System Vars
5555
default:
56-
err = mc.exec("SET " + param + "=" + val + "")
56+
err = mc.exec(backgroundCtx(), "SET "+param+"="+val+"")
5757
if err != nil {
5858
return
5959
}
@@ -63,12 +63,17 @@ func (mc *mysqlConn) handleParams() (err error) {
6363
return
6464
}
6565

66+
// Begin implements driver.Conn interface
6667
func (mc *mysqlConn) Begin() (driver.Tx, error) {
68+
return mc.beginTx(backgroundCtx(), txOptions{})
69+
}
70+
71+
func (mc *mysqlConn) beginTx(ctx mysqlContext, opts txOptions) (driver.Tx, error) {
6772
if mc.netConn == nil {
6873
errLog.Print(ErrInvalidConn)
6974
return nil, driver.ErrBadConn
7075
}
71-
err := mc.exec("START TRANSACTION")
76+
err := mc.exec(ctx, "START TRANSACTION")
7277
if err == nil {
7378
return &mysqlTx{mc}, err
7479
}
@@ -79,7 +84,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
7984
func (mc *mysqlConn) Close() (err error) {
8085
// Makes Close idempotent
8186
if mc.netConn != nil {
82-
err = mc.writeCommandPacket(comQuit)
87+
err = mc.writeCommandPacket(backgroundCtx(), comQuit)
8388
}
8489

8590
mc.cleanup()
@@ -104,12 +109,16 @@ func (mc *mysqlConn) cleanup() {
104109
}
105110

106111
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
112+
return mc.prepareContext(backgroundCtx(), query)
113+
}
114+
115+
func (mc *mysqlConn) prepareContext(ctx mysqlContext, query string) (driver.Stmt, error) {
107116
if mc.netConn == nil {
108117
errLog.Print(ErrInvalidConn)
109118
return nil, driver.ErrBadConn
110119
}
111120
// Send command
112-
err := mc.writeCommandPacketStr(comStmtPrepare, query)
121+
err := mc.writeCommandPacketStr(ctx, comStmtPrepare, query)
113122
if err != nil {
114123
return nil, err
115124
}
@@ -258,6 +267,10 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
258267
}
259268

260269
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
270+
return mc.ExecContext(backgroundCtx(), query, args)
271+
}
272+
273+
func (mc *mysqlConn) ExecContext(ctx mysqlContext, query string, args []driver.Value) (driver.Result, error) {
261274
if mc.netConn == nil {
262275
errLog.Print(ErrInvalidConn)
263276
return nil, driver.ErrBadConn
@@ -276,7 +289,7 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
276289
mc.affectedRows = 0
277290
mc.insertId = 0
278291

279-
err := mc.exec(query)
292+
err := mc.exec(ctx, query)
280293
if err == nil {
281294
return &mysqlResult{
282295
affectedRows: int64(mc.affectedRows),
@@ -287,9 +300,9 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
287300
}
288301

289302
// Internal function to execute commands
290-
func (mc *mysqlConn) exec(query string) error {
303+
func (mc *mysqlConn) exec(ctx mysqlContext, query string) error {
291304
// Send command
292-
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
305+
if err := mc.writeCommandPacketStr(ctx, comQuery, query); err != nil {
293306
return err
294307
}
295308

@@ -314,7 +327,12 @@ func (mc *mysqlConn) exec(query string) error {
314327
return mc.discardResults()
315328
}
316329

330+
// Query implements driver.Queryer interface
317331
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
332+
return mc.queryContext(backgroundCtx(), query, args)
333+
}
334+
335+
func (mc *mysqlConn) queryContext(ctx mysqlContext, query string, args []driver.Value) (driver.Rows, error) {
318336
if mc.netConn == nil {
319337
errLog.Print(ErrInvalidConn)
320338
return nil, driver.ErrBadConn
@@ -331,7 +349,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
331349
query = prepared
332350
}
333351
// Send command
334-
err := mc.writeCommandPacketStr(comQuery, query)
352+
err := mc.writeCommandPacketStr(ctx, comQuery, query)
335353
if err == nil {
336354
// Read Result
337355
var resLen int
@@ -362,7 +380,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
362380
// The returned byte slice is only valid until the next read
363381
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
364382
// Send command
365-
if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
383+
if err := mc.writeCommandPacketStr(backgroundCtx(), comQuery, "SELECT @@"+name); err != nil {
366384
return nil, err
367385
}
368386

connection_ctx.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// +build go1.8
2+
3+
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
4+
//
5+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
6+
//
7+
// This Source Code Form is subject to the terms of the Mozilla Public
8+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
9+
// You can obtain one at http://mozilla.org/MPL/2.0/.
10+
11+
package mysql
12+
13+
import (
14+
"context"
15+
"database/sql/driver"
16+
)
17+
18+
// Ping implements driver.Pinger interface
19+
func (mc *mysqlConn) Ping(ctx context.Context) error {
20+
if mc.netConn == nil {
21+
errLog.Print(ErrInvalidConn)
22+
return driver.ErrBadConn
23+
}
24+
if err := mc.writeCommandPacket(ctx, comPing); err != nil {
25+
errLog.Print(err)
26+
return err
27+
}
28+
29+
if _, err := mc.readResultOK(); err != nil {
30+
errLog.Print(err)
31+
return err
32+
}
33+
return nil
34+
}
35+
36+
// BeginTx implements driver.ConnBeginTx interface
37+
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
38+
return mc.beginTx(ctx, txOptions(opts))
39+
}
40+
41+
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
42+
return mc.prepareContext(ctx, query)
43+
}
44+
45+
// QueryContext implements driver.QueryerContext interface
46+
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.Value) (driver.Rows, error) {
47+
return mc.queryContext(ctx, query, args)
48+
}

ctx_backport.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// +build !go1.8
2+
3+
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
4+
//
5+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
6+
//
7+
// This Source Code Form is subject to the terms of the Mozilla Public
8+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
9+
// You can obtain one at http://mozilla.org/MPL/2.0/.
10+
11+
package mysql
12+
13+
import (
14+
"time"
15+
)
16+
17+
// txOptions is defined for compatibility with Go 1.8's driver.TxOptions struct.
18+
type txOptions struct {
19+
Isolation int
20+
ReadOnly bool
21+
}
22+
23+
// mysqlContext is a copy of context.Context from Go 1.7 and later.
24+
type mysqlContext interface {
25+
Deadline() (deadline time.Time, ok bool)
26+
27+
Done() <-chan struct{}
28+
29+
Err() error
30+
31+
Value(key interface{}) interface{}
32+
}
33+
34+
// emptyCtx is copied from Go 1.7's context package.
35+
type emptyCtx int
36+
37+
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
38+
return
39+
}
40+
41+
func (*emptyCtx) Done() <-chan struct{} {
42+
return nil
43+
}
44+
45+
func (*emptyCtx) Err() error {
46+
return nil
47+
}
48+
49+
func (*emptyCtx) Value(key interface{}) interface{} {
50+
return nil
51+
}
52+
53+
func (e *emptyCtx) String() string {
54+
return "context.Background"
55+
}
56+
57+
var background = new(emptyCtx)
58+
59+
func backgroundCtx() mysqlContext {
60+
return background
61+
}

ctx_go18.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// +build go1.8
2+
3+
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
4+
//
5+
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
6+
//
7+
// This Source Code Form is subject to the terms of the Mozilla Public
8+
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
9+
// You can obtain one at http://mozilla.org/MPL/2.0/.
10+
11+
package mysql
12+
13+
import (
14+
"context"
15+
"database/sql/driver"
16+
)
17+
18+
// The definitions below are for compatibility with older Go versions.
19+
// See ctx_backport.go for the definitions used in older Go versions.
20+
21+
type txOptions driver.TxOptions
22+
23+
type mysqlContext context.Context
24+
25+
func backgroundCtx() mysqlContext {
26+
return context.Background()
27+
}

driver.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// db, err := sql.Open("mysql", "user:password@/dbname")
1515
//
1616
// See https://github.com/go-sql-driver/mysql#usage for details
17+
1718
package mysql
1819

1920
import (
@@ -95,7 +96,7 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
9596
}
9697

9798
// Send Client Authentication Packet
98-
if err = mc.writeAuthPacket(cipher); err != nil {
99+
if err = mc.writeAuthPacket(backgroundCtx(), cipher); err != nil {
99100
mc.cleanup()
100101
return nil, err
101102
}
@@ -157,20 +158,20 @@ func handleAuthResult(mc *mysqlConn, oldCipher []byte) error {
157158
cipher = oldCipher
158159
}
159160

160-
if err = mc.writeOldAuthPacket(cipher); err != nil {
161+
if err = mc.writeOldAuthPacket(backgroundCtx(), cipher); err != nil {
161162
return err
162163
}
163164
_, err = mc.readResultOK()
164165
} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
165166
// Retry with clear text password for
166167
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
167168
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
168-
if err = mc.writeClearAuthPacket(); err != nil {
169+
if err = mc.writeClearAuthPacket(backgroundCtx()); err != nil {
169170
return err
170171
}
171172
_, err = mc.readResultOK()
172173
} else if mc.cfg.AllowNativePasswords && err == ErrNativePassword {
173-
if err = mc.writeNativeAuthPacket(cipher); err != nil {
174+
if err = mc.writeNativeAuthPacket(backgroundCtx(), cipher); err != nil {
174175
return err
175176
}
176177
_, err = mc.readResultOK()

driver_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ func TestEmptyQuery(t *testing.T) {
182182
})
183183
}
184184

185+
func (dbt *DBTest) TestPing(t *testing.T) {
186+
runTests(t, dsn, func(dbt *DBTest) {
187+
if err := dbt.db.Ping(); err != nil {
188+
dbt.fail("Ping", "Ping", err)
189+
}
190+
})
191+
}
192+
185193
func TestCRUD(t *testing.T) {
186194
runTests(t, dsn, func(dbt *DBTest) {
187195
// Create Table

infile.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func deferredClose(err *error, closer io.Closer) {
9393
}
9494
}
9595

96-
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
96+
func (mc *mysqlConn) handleInFileRequest(ctx mysqlContext, name string) (err error) {
9797
var rdr io.Reader
9898
var data []byte
9999
packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
@@ -153,7 +153,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
153153
for err == nil {
154154
n, err = rdr.Read(data[4:])
155155
if n > 0 {
156-
if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
156+
if ioErr := mc.writePacket(ctx, data[:4+n]); ioErr != nil {
157157
return ioErr
158158
}
159159
}
@@ -167,7 +167,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
167167
if data == nil {
168168
data = make([]byte, 4)
169169
}
170-
if ioErr := mc.writePacket(data[:4]); ioErr != nil {
170+
if ioErr := mc.writePacket(ctx, data[:4]); ioErr != nil {
171171
return ioErr
172172
}
173173

0 commit comments

Comments
 (0)