@@ -10,13 +10,23 @@ package mysql
10
10
11
11
import (
12
12
"database/sql/driver"
13
+ "errors"
13
14
"io"
14
15
"net"
15
16
"strconv"
16
17
"strings"
18
+ "sync"
17
19
"time"
18
20
)
19
21
22
+ //a copy of context.Context from Go 1.7 and later.
23
+ type mysqlContext interface {
24
+ Deadline () (deadline time.Time , ok bool )
25
+ Done () <- chan struct {}
26
+ Err () error
27
+ Value (key interface {}) interface {}
28
+ }
29
+
20
30
type mysqlConn struct {
21
31
buf buffer
22
32
netConn net.Conn
@@ -31,6 +41,13 @@ type mysqlConn struct {
31
41
sequence uint8
32
42
parseTime bool
33
43
strict bool
44
+ watcher chan <- mysqlContext
45
+ closech chan struct {}
46
+ finished chan <- struct {}
47
+
48
+ mu sync.Mutex // guards following fields
49
+ closed error // set non-nil when conn is closed, before closech is closed
50
+ canceledErr error // set non-nil if conn is canceled
34
51
}
35
52
36
53
// Handles parameters set in DSN after the connection is established
@@ -64,7 +81,7 @@ func (mc *mysqlConn) handleParams() (err error) {
64
81
}
65
82
66
83
func (mc * mysqlConn ) Begin () (driver.Tx , error ) {
67
- if mc .netConn == nil {
84
+ if mc .isBroken () {
68
85
errLog .Print (ErrInvalidConn )
69
86
return nil , driver .ErrBadConn
70
87
}
@@ -78,11 +95,11 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
78
95
79
96
func (mc * mysqlConn ) Close () (err error ) {
80
97
// Makes Close idempotent
81
- if mc .netConn != nil {
98
+ if ! mc .isBroken () {
82
99
err = mc .writeCommandPacket (comQuit )
83
100
}
84
101
85
- mc .cleanup ()
102
+ mc .cleanup (errors . New ( "mysql: connection is closed" ) )
86
103
87
104
return
88
105
}
@@ -91,20 +108,36 @@ func (mc *mysqlConn) Close() (err error) {
91
108
// function after successfully authentication, call Close instead. This function
92
109
// is called before auth or on auth failure because MySQL will have already
93
110
// closed the network connection.
94
- func (mc * mysqlConn ) cleanup () {
111
+ func (mc * mysqlConn ) cleanup (err error ) {
112
+ if err == nil {
113
+ panic ("nil error" )
114
+ }
115
+ mc .mu .Lock ()
116
+ defer mc .mu .Unlock ()
117
+
118
+ if mc .closed != nil {
119
+ return
120
+ }
121
+
95
122
// Makes cleanup idempotent
96
- if mc .netConn != nil {
97
- if err := mc .netConn .Close (); err != nil {
98
- errLog .Print (err )
99
- }
100
- mc .netConn = nil
123
+ mc .closed = err
124
+ close (mc .closech )
125
+ if mc .netConn == nil {
126
+ return
127
+ }
128
+ if err := mc .netConn .Close (); err != nil {
129
+ errLog .Print (err )
101
130
}
102
- mc .cfg = nil
103
- mc .buf .nc = nil
131
+ }
132
+
133
+ func (mc * mysqlConn ) isBroken () bool {
134
+ mc .mu .Lock ()
135
+ defer mc .mu .Unlock ()
136
+ return mc .closed != nil
104
137
}
105
138
106
139
func (mc * mysqlConn ) Prepare (query string ) (driver.Stmt , error ) {
107
- if mc .netConn == nil {
140
+ if mc .isBroken () {
108
141
errLog .Print (ErrInvalidConn )
109
142
return nil , driver .ErrBadConn
110
143
}
@@ -258,7 +291,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
258
291
}
259
292
260
293
func (mc * mysqlConn ) Exec (query string , args []driver.Value ) (driver.Result , error ) {
261
- if mc .netConn == nil {
294
+ if mc .isBroken () {
262
295
errLog .Print (ErrInvalidConn )
263
296
return nil , driver .ErrBadConn
264
297
}
@@ -315,7 +348,7 @@ func (mc *mysqlConn) exec(query string) error {
315
348
}
316
349
317
350
func (mc * mysqlConn ) Query (query string , args []driver.Value ) (driver.Rows , error ) {
318
- if mc .netConn == nil {
351
+ if mc .isBroken () {
319
352
errLog .Print (ErrInvalidConn )
320
353
return nil , driver .ErrBadConn
321
354
}
@@ -387,3 +420,29 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
387
420
}
388
421
return nil , err
389
422
}
423
+
424
+ // finish is called when the query has canceled.
425
+ func (mc * mysqlConn ) cancel (err error ) {
426
+ mc .mu .Lock ()
427
+ mc .canceledErr = err
428
+ mc .mu .Unlock ()
429
+ mc .cleanup (errors .New ("mysql: query canceled" ))
430
+ }
431
+
432
+ // canceled returns non-nil if the connection was closed due to context cancelation.
433
+ func (mc * mysqlConn ) canceled () error {
434
+ mc .mu .Lock ()
435
+ defer mc .mu .Unlock ()
436
+ return mc .canceledErr
437
+ }
438
+
439
+ // finish is called when the query has succeeded.
440
+ func (mc * mysqlConn ) finish () {
441
+ if mc .finished == nil {
442
+ return
443
+ }
444
+ select {
445
+ case mc .finished <- struct {}{}:
446
+ case <- mc .closech :
447
+ }
448
+ }
0 commit comments