@@ -3,8 +3,11 @@ package driver
33import (
44 "context"
55 "database/sql"
6+ sqlDriver "database/sql/driver"
67 "fmt"
8+ "math"
79 "net"
10+ "reflect"
811 "strings"
912 "testing"
1013 "time"
@@ -94,16 +97,85 @@ func TestDriverOptions_writeTimeout(t *testing.T) {
9497 srv := CreateMockServer (t )
9598 defer srv .Stop ()
9699
100+ // use a writeTimeout that will fail parsing by ParseDuration resulting
101+ // in a conn open error. The Open() won't fail until the ExecContext()
102+ // call though, because that's when golang database/sql package will open
103+ // the actual connection.
97104 conn ,
err := sql .
Open (
"mysql" ,
"[email protected] :3307/test?writeTimeout=10" )
98105 require .NoError (t , err )
106+ require .NotNil (t , conn )
99107
100- result , err := conn .ExecContext (context .TODO (), "insert into slow(a,b) values(1,2);" )
108+ // here we should fail because of the missing time unit in the duration.
109+ result , err := conn .ExecContext (context .TODO (), "select 1;" )
110+ require .Contains (t , err .Error (), "missing unit in duration" )
111+ require .Error (t , err )
101112 require .Nil (t , result )
113+
114+ // use an almost zero (1ns) writeTimeout to ensure the insert statement
115+ // can't write before the timeout. Just want to make sure ExecContext()
116+ // will throw an error.
117+ conn ,
err = sql .
Open (
"mysql" ,
"[email protected] :3307/test?writeTimeout=1ns" )
118+ require .NoError (t , err )
119+
120+ // ExecContext() should fail due to the write timeout of 1ns
121+ result , err = conn .ExecContext (context .TODO (), "insert into slow(a,b) values(1,2);" )
102122 require .Error (t , err )
123+ require .Contains (t , err .Error (), "i/o timeout" )
124+ require .Nil (t , result )
103125
104126 conn .Close ()
105127}
106128
129+ func TestDriverOptions_namedValueChecker (t * testing.T ) {
130+ AddNamedValueChecker (func (nv * sqlDriver.NamedValue ) error {
131+ rv := reflect .ValueOf (nv .Value )
132+ if rv .Kind () != reflect .Uint64 {
133+ // fallback to the default value converter when the value is not a uint64
134+ return sqlDriver .ErrSkip
135+ }
136+
137+ return nil
138+ })
139+
140+ log .SetLevel (log .LevelDebug )
141+ srv := CreateMockServer (t )
142+ defer srv .Stop ()
143+ conn ,
err := sql .
Open (
"mysql" ,
"[email protected] :3307/test?writeTimeout=1s" )
144+ require .NoError (t , err )
145+ defer conn .Close ()
146+
147+ // the NamedValueChecker will return ErrSkip for types that are NOT uint64, so make
148+ // sure those make it to the server ok first.
149+ int32Stmt , err := conn .Prepare ("select ?" )
150+ require .NoError (t , err )
151+ defer int32Stmt .Close ()
152+ r1 , err := int32Stmt .Query (math .MaxInt32 )
153+ require .NoError (t , err )
154+ require .NotNil (t , r1 )
155+
156+ var i32 int32
157+ require .True (t , r1 .Next ())
158+ require .NoError (t , r1 .Scan (& i32 ))
159+ require .True (t , math .MaxInt32 == i32 )
160+
161+ // Now make sure that the uint64 makes it to the server as well, this case will be handled
162+ // by the NamedValueChecker (i.e. it will not return ErrSkip)
163+ stmt , err := conn .Prepare ("select a, b from fast where uint64 = ?" )
164+ require .NoError (t , err )
165+ defer stmt .Close ()
166+
167+ var val uint64 = math .MaxUint64
168+ result , err := stmt .Query (val )
169+ require .NoError (t , err )
170+ require .NotNil (t , result )
171+
172+ var a uint64
173+ var b string
174+ require .True (t , result .Next ())
175+ require .NoError (t , result .Scan (& a , & b ))
176+ require .True (t , math .MaxUint64 == a )
177+ }
178+
107179func CreateMockServer (t * testing.T ) * testServer {
108180 inMemProvider := server .NewInMemoryProvider ()
109181 inMemProvider .AddUser (* testUser , * testPassword )
@@ -151,7 +223,7 @@ func (h *mockHandler) UseDB(dbName string) error {
151223 return nil
152224}
153225
154- func (h * mockHandler ) handleQuery (query string , binary bool ) (* mysql.Result , error ) {
226+ func (h * mockHandler ) handleQuery (query string , binary bool , args [] interface {} ) (* mysql.Result , error ) {
155227 ss := strings .Split (query , " " )
156228 switch strings .ToLower (ss [0 ]) {
157229 case "select" :
@@ -163,13 +235,24 @@ func (h *mockHandler) handleQuery(query string, binary bool) (*mysql.Result, err
163235 {mysql .MaxPayloadLen },
164236 }, binary )
165237 } else {
166- if strings .Contains (query , "slow" ) {
167- time .Sleep (time .Second * 5 )
168- }
238+ if ss [1 ] == "?" {
239+ r , err = mysql .BuildSimpleResultset ([]string {"a" }, [][]interface {}{
240+ {args [0 ].(int64 )},
241+ }, binary )
242+ } else {
243+ if strings .Contains (query , "slow" ) {
244+ time .Sleep (time .Second * 5 )
245+ }
169246
170- r , err = mysql .BuildSimpleResultset ([]string {"a" , "b" }, [][]interface {}{
171- {1 , "hello world" },
172- }, binary )
247+ var aValue uint64 = 1
248+ if strings .Contains (query , "uint64" ) {
249+ aValue = math .MaxUint64
250+ }
251+
252+ r , err = mysql .BuildSimpleResultset ([]string {"a" , "b" }, [][]interface {}{
253+ {aValue , "hello world" },
254+ }, binary )
255+ }
173256 }
174257
175258 if err != nil {
@@ -197,7 +280,7 @@ func (h *mockHandler) handleQuery(query string, binary bool) (*mysql.Result, err
197280}
198281
199282func (h * mockHandler ) HandleQuery (query string ) (* mysql.Result , error ) {
200- return h .handleQuery (query , false )
283+ return h .handleQuery (query , false , nil )
201284}
202285
203286func (h * mockHandler ) HandleFieldList (table string , fieldWildcard string ) ([]* mysql.Field , error ) {
@@ -206,13 +289,13 @@ func (h *mockHandler) HandleFieldList(table string, fieldWildcard string) ([]*my
206289
207290func (h * mockHandler ) HandleStmtPrepare (query string ) (params int , columns int , context interface {}, err error ) {
208291 params = 1
209- columns = 0
292+ columns = 2
210293 return params , columns , nil , nil
211294}
212295
213296func (h * mockHandler ) HandleStmtExecute (context interface {}, query string , args []interface {}) (* mysql.Result , error ) {
214297 if strings .HasPrefix (strings .ToLower (query ), "select" ) {
215- return h .HandleQuery (query )
298+ return h .handleQuery (query , true , args )
216299 }
217300
218301 return & mysql.Result {
0 commit comments