blob: 112f280ec5364ab8600ac46df9ac1f549f6c65b0 [file] [log] [blame]
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -07001// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package sql
6
7import (
Brad Fitzpatrick7fc4c072012-01-19 16:04:26 -08008 "database/sql/driver"
Russ Coxc2049d22011-11-01 22:04:37 -04009 "errors"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070010 "fmt"
Russ Coxc2049d22011-11-01 22:04:37 -040011 "io"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070012 "log"
Russ Cox5318a1b2014-10-15 13:10:14 -040013 "sort"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070014 "strconv"
15 "strings"
16 "sync"
Brad Fitzpatrick36d3bef2013-04-15 14:06:41 -070017 "testing"
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -080018 "time"
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070019)
20
21var _ = log.Printf
22
23// fakeDriver is a fake database that implements Go's driver.Driver
24// interface, just for testing.
25//
26// It speaks a query language that's semantically similar to but
Arne Hormanna1a3d212013-12-18 08:17:43 +110027// syntactically different and simpler than SQL. The syntax is as
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070028// follows:
29//
30// WIPE
31// CREATE|<tablename>|<col>=<type>,<col>=<type>,...
32// where types are: "string", [u]int{8,16,32,64}, "bool"
33// INSERT|<tablename>|col=val,col2=val2,col3=?
34// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
35//
David G. Andersene66d29c2012-07-09 09:16:10 +100036// When opening a fakeDriver's database, it starts empty with no
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070037// tables. All tables and data are stored in memory only.
38type fakeDriver struct {
Brad Fitzpatrick277047f2013-04-25 14:45:56 -070039 mu sync.Mutex // guards 3 following fields
40 openCount int // conn opens
41 closeCount int // conn closes
Alberto GarcĂ­a Hierro37db8802013-10-16 09:22:57 -070042 waitCh chan struct{}
43 waitingCh chan struct{}
Brad Fitzpatrick277047f2013-04-25 14:45:56 -070044 dbs map[string]*fakeDB
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070045}
46
47type fakeDB struct {
48 name string
49
James David Chalfant19e2f262012-12-14 09:00:33 -080050 mu sync.Mutex
51 free []*fakeConn
52 tables map[string]*table
53 badConn bool
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -070054}
55
56type table struct {
57 mu sync.Mutex
58 colname []string
59 coltype []string
60 rows []*row
61}
62
63func (t *table) columnIndex(name string) int {
64 for n, nname := range t.colname {
65 if name == nname {
66 return n
67 }
68 }
69 return -1
70}
71
72type row struct {
73 cols []interface{} // must be same size as its table colname + coltype
74}
75
76func (r *row) clone() *row {
77 nrow := &row{cols: make([]interface{}, len(r.cols))}
78 copy(nrow.cols, r.cols)
79 return nrow
80}
81
82type fakeConn struct {
83 db *fakeDB // where to return ourselves to
84
85 currTx *fakeTx
Brad Fitzpatrick1c441e22012-01-13 15:25:07 -080086
87 // Stats for tests:
88 mu sync.Mutex
89 stmtsMade int
90 stmtsClosed int
Brad Fitzpatrick48eacd92012-03-06 14:10:58 -080091 numPrepare int
Marko Tiikkajac468f9462015-03-27 19:45:12 +010092
93 // bad connection tests; see isBad()
94 bad bool
95 stickyBad bool
Brad Fitzpatrick1c441e22012-01-13 15:25:07 -080096}
97
98func (c *fakeConn) incrStat(v *int) {
99 c.mu.Lock()
100 *v++
101 c.mu.Unlock()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700102}
103
104type fakeTx struct {
105 c *fakeConn
106}
107
108type fakeStmt struct {
109 c *fakeConn
110 q string // just for debugging
111
112 cmd string
113 table string
114
Brad Fitzpatrick750d0e32011-11-20 14:56:49 -0500115 closed bool
116
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700117 colName []string // used by CREATE, INSERT, SELECT (selected columns)
118 colType []string // used by CREATE
119 colValue []interface{} // used by INSERT (mix of strings and "?" for bound params)
120 placeholders int // used by INSERT/SELECT: number of ? params
121
122 whereCol []string // used by SELECT (all placeholders)
123
124 placeholderConverter []driver.ValueConverter // used by INSERT
125}
126
127var fdriver driver.Driver = &fakeDriver{}
128
129func init() {
130 Register("test", fdriver)
131}
132
Russ Cox5318a1b2014-10-15 13:10:14 -0400133func contains(list []string, y string) bool {
134 for _, x := range list {
135 if x == y {
136 return true
137 }
138 }
139 return false
140}
141
142type Dummy struct {
143 driver.Driver
144}
145
146func TestDrivers(t *testing.T) {
Brad Fitzpatrick9dc1cce2014-10-31 09:49:42 -0700147 unregisterAllDrivers()
148 Register("test", fdriver)
Russ Cox5318a1b2014-10-15 13:10:14 -0400149 Register("invalid", Dummy{})
150 all := Drivers()
151 if len(all) < 2 || !sort.StringsAreSorted(all) || !contains(all, "test") || !contains(all, "invalid") {
152 t.Fatalf("Drivers = %v, want sorted list with at least [invalid, test]", all)
153 }
154}
155
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700156// Supports dsn forms:
157// <dbname>
James David Chalfant19e2f262012-12-14 09:00:33 -0800158// <dbname>;<opts> (only currently supported option is `badConn`,
159// which causes driver.ErrBadConn to be returned on
160// every other conn.Begin())
Russ Coxc2049d22011-11-01 22:04:37 -0400161func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700162 parts := strings.Split(dsn, ";")
163 if len(parts) < 1 {
Russ Coxc2049d22011-11-01 22:04:37 -0400164 return nil, errors.New("fakedb: no database name")
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700165 }
166 name := parts[0]
Brad Fitzpatrick4435c8b2012-01-10 12:51:27 -0800167
168 db := d.getDB(name)
169
170 d.mu.Lock()
171 d.openCount++
172 d.mu.Unlock()
James David Chalfant19e2f262012-12-14 09:00:33 -0800173 conn := &fakeConn{db: db}
174
175 if len(parts) >= 2 && parts[1] == "badConn" {
176 conn.bad = true
177 }
Alberto GarcĂ­a Hierro37db8802013-10-16 09:22:57 -0700178 if d.waitCh != nil {
179 d.waitingCh <- struct{}{}
180 <-d.waitCh
Alberto GarcĂ­a Hierroe39eda12013-10-17 09:02:32 -0700181 d.waitCh = nil
182 d.waitingCh = nil
Alberto GarcĂ­a Hierro37db8802013-10-16 09:22:57 -0700183 }
James David Chalfant19e2f262012-12-14 09:00:33 -0800184 return conn, nil
Brad Fitzpatrick4435c8b2012-01-10 12:51:27 -0800185}
186
187func (d *fakeDriver) getDB(name string) *fakeDB {
188 d.mu.Lock()
189 defer d.mu.Unlock()
190 if d.dbs == nil {
191 d.dbs = make(map[string]*fakeDB)
192 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700193 db, ok := d.dbs[name]
194 if !ok {
195 db = &fakeDB{name: name}
196 d.dbs[name] = db
197 }
Brad Fitzpatrick4435c8b2012-01-10 12:51:27 -0800198 return db
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700199}
200
201func (db *fakeDB) wipe() {
202 db.mu.Lock()
203 defer db.mu.Unlock()
204 db.tables = nil
205}
206
Russ Coxc2049d22011-11-01 22:04:37 -0400207func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700208 db.mu.Lock()
209 defer db.mu.Unlock()
210 if db.tables == nil {
211 db.tables = make(map[string]*table)
212 }
213 if _, exist := db.tables[name]; exist {
214 return fmt.Errorf("table %q already exists", name)
215 }
216 if len(columnNames) != len(columnTypes) {
217 return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d",
Robert Henckec5018242011-10-13 13:34:01 +1100218 name, len(columnNames), len(columnTypes))
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700219 }
220 db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
221 return nil
222}
223
224// must be called with db.mu lock held
225func (db *fakeDB) table(table string) (*table, bool) {
226 if db.tables == nil {
227 return nil, false
228 }
229 t, ok := db.tables[table]
230 return t, ok
231}
232
233func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
234 db.mu.Lock()
235 defer db.mu.Unlock()
236 t, ok := db.table(table)
237 if !ok {
238 return
239 }
240 for n, cname := range t.colname {
241 if cname == column {
242 return t.coltype[n], true
243 }
244 }
245 return "", false
246}
247
James David Chalfant19e2f262012-12-14 09:00:33 -0800248func (c *fakeConn) isBad() bool {
Marko Tiikkajac468f9462015-03-27 19:45:12 +0100249 if c.stickyBad {
250 return true
251 } else if c.bad {
252 // alternate between bad conn and not bad conn
253 c.db.badConn = !c.db.badConn
254 return c.db.badConn
255 } else {
James David Chalfant19e2f262012-12-14 09:00:33 -0800256 return false
257 }
James David Chalfant19e2f262012-12-14 09:00:33 -0800258}
259
Russ Coxc2049d22011-11-01 22:04:37 -0400260func (c *fakeConn) Begin() (driver.Tx, error) {
James David Chalfant19e2f262012-12-14 09:00:33 -0800261 if c.isBad() {
262 return nil, driver.ErrBadConn
263 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700264 if c.currTx != nil {
Russ Coxc2049d22011-11-01 22:04:37 -0400265 return nil, errors.New("already in a transaction")
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700266 }
267 c.currTx = &fakeTx{c: c}
268 return c.currTx, nil
269}
270
Brad Fitzpatrick209f6b12013-03-25 16:50:27 -0700271var hookPostCloseConn struct {
272 sync.Mutex
273 fn func(*fakeConn, error)
274}
275
276func setHookpostCloseConn(fn func(*fakeConn, error)) {
277 hookPostCloseConn.Lock()
278 defer hookPostCloseConn.Unlock()
279 hookPostCloseConn.fn = fn
280}
281
Brad Fitzpatrick36d3bef2013-04-15 14:06:41 -0700282var testStrictClose *testing.T
283
284// setStrictFakeConnClose sets the t to Errorf on when fakeConn.Close
285// fails to close. If nil, the check is disabled.
286func setStrictFakeConnClose(t *testing.T) {
287 testStrictClose = t
288}
289
Brad Fitzpatrick209f6b12013-03-25 16:50:27 -0700290func (c *fakeConn) Close() (err error) {
Brad Fitzpatrick277047f2013-04-25 14:45:56 -0700291 drv := fdriver.(*fakeDriver)
Brad Fitzpatrick209f6b12013-03-25 16:50:27 -0700292 defer func() {
Brad Fitzpatrick36d3bef2013-04-15 14:06:41 -0700293 if err != nil && testStrictClose != nil {
294 testStrictClose.Errorf("failed to close a test fakeConn: %v", err)
295 }
Brad Fitzpatrick209f6b12013-03-25 16:50:27 -0700296 hookPostCloseConn.Lock()
297 fn := hookPostCloseConn.fn
298 hookPostCloseConn.Unlock()
299 if fn != nil {
300 fn(c, err)
301 }
Brad Fitzpatrick277047f2013-04-25 14:45:56 -0700302 if err == nil {
303 drv.mu.Lock()
304 drv.closeCount++
305 drv.mu.Unlock()
306 }
Brad Fitzpatrick209f6b12013-03-25 16:50:27 -0700307 }()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700308 if c.currTx != nil {
Brad Fitzpatrick3297fc62012-03-10 10:00:02 -0800309 return errors.New("can't close fakeConn; in a Transaction")
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700310 }
311 if c.db == nil {
Brad Fitzpatrick3297fc62012-03-10 10:00:02 -0800312 return errors.New("can't close fakeConn; already closed")
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700313 }
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800314 if c.stmtsMade > c.stmtsClosed {
315 return errors.New("can't close; dangling statement(s)")
316 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700317 c.db = nil
318 return nil
319}
320
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100321func checkSubsetTypes(args []driver.Value) error {
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800322 for n, arg := range args {
323 switch arg.(type) {
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800324 case int64, float64, bool, nil, []byte, string, time.Time:
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800325 default:
326 return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
327 }
328 }
329 return nil
330}
331
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100332func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800333 // This is an optional interface, but it's implemented here
David G. Andersene66d29c2012-07-09 09:16:10 +1000334 // just to check that all the args are of the proper types.
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800335 // ErrSkip is returned so the caller acts as if we didn't
336 // implement this at all.
337 err := checkSubsetTypes(args)
338 if err != nil {
339 return nil, err
340 }
341 return nil, driver.ErrSkip
342}
343
Julien Schmidt2968e232013-02-13 15:25:39 -0800344func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
345 // This is an optional interface, but it's implemented here
346 // just to check that all the args are of the proper types.
347 // ErrSkip is returned so the caller acts as if we didn't
348 // implement this at all.
349 err := checkSubsetTypes(args)
350 if err != nil {
351 return nil, err
352 }
353 return nil, driver.ErrSkip
354}
355
Russ Coxc2049d22011-11-01 22:04:37 -0400356func errf(msg string, args ...interface{}) error {
357 return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700358}
359
360// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
David G. Andersene66d29c2012-07-09 09:16:10 +1000361// (note that where columns must always contain ? marks,
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700362// just a limitation for fakedb)
Russ Coxc2049d22011-11-01 22:04:37 -0400363func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700364 if len(parts) != 3 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800365 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700366 return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
367 }
368 stmt.table = parts[0]
369 stmt.colName = strings.Split(parts[1], ",")
370 for n, colspec := range strings.Split(parts[2], ",") {
Brad Fitzpatrick750d0e32011-11-20 14:56:49 -0500371 if colspec == "" {
372 continue
373 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700374 nameVal := strings.Split(colspec, "=")
375 if len(nameVal) != 2 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800376 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700377 return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
378 }
379 column, value := nameVal[0], nameVal[1]
380 _, ok := c.db.columnType(stmt.table, column)
381 if !ok {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800382 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700383 return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
384 }
385 if value != "?" {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800386 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700387 return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
388 stmt.table, column)
389 }
390 stmt.whereCol = append(stmt.whereCol, column)
391 stmt.placeholders++
392 }
393 return stmt, nil
394}
395
396// parts are table|col=type,col2=type2
Russ Coxc2049d22011-11-01 22:04:37 -0400397func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700398 if len(parts) != 2 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800399 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700400 return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
401 }
402 stmt.table = parts[0]
403 for n, colspec := range strings.Split(parts[1], ",") {
404 nameType := strings.Split(colspec, "=")
405 if len(nameType) != 2 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800406 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700407 return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
408 }
409 stmt.colName = append(stmt.colName, nameType[0])
410 stmt.colType = append(stmt.colType, nameType[1])
411 }
412 return stmt, nil
413}
414
415// parts are table|col=?,col2=val
Russ Coxc2049d22011-11-01 22:04:37 -0400416func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700417 if len(parts) != 2 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800418 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700419 return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
420 }
421 stmt.table = parts[0]
422 for n, colspec := range strings.Split(parts[1], ",") {
423 nameVal := strings.Split(colspec, "=")
424 if len(nameVal) != 2 {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800425 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700426 return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
427 }
428 column, value := nameVal[0], nameVal[1]
429 ctype, ok := c.db.columnType(stmt.table, column)
430 if !ok {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800431 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700432 return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
433 }
434 stmt.colName = append(stmt.colName, column)
435
436 if value != "?" {
437 var subsetVal interface{}
438 // Convert to driver subset type
439 switch ctype {
440 case "string":
441 subsetVal = []byte(value)
Brad Fitzpatrick701f70a2012-01-12 11:23:33 -0800442 case "blob":
443 subsetVal = []byte(value)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700444 case "int32":
445 i, err := strconv.Atoi(value)
446 if err != nil {
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800447 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700448 return nil, errf("invalid conversion to int32 from %q", value)
449 }
450 subsetVal = int64(i) // int64 is a subset type, but not int32
451 default:
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800452 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700453 return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
454 }
455 stmt.colValue = append(stmt.colValue, subsetVal)
456 } else {
457 stmt.placeholders++
458 stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
459 stmt.colValue = append(stmt.colValue, "?")
460 }
461 }
462 return stmt, nil
463}
464
Julien Schmidt762a9d92013-12-17 11:57:30 -0800465// hook to simulate broken connections
466var hookPrepareBadConn func() bool
467
Russ Coxc2049d22011-11-01 22:04:37 -0400468func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
Brad Fitzpatrick48eacd92012-03-06 14:10:58 -0800469 c.numPrepare++
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700470 if c.db == nil {
471 panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
472 }
Julien Schmidt762a9d92013-12-17 11:57:30 -0800473
Marko Tiikkajac468f9462015-03-27 19:45:12 +0100474 if c.stickyBad || (hookPrepareBadConn != nil && hookPrepareBadConn()) {
Julien Schmidt762a9d92013-12-17 11:57:30 -0800475 return nil, driver.ErrBadConn
476 }
477
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700478 parts := strings.Split(query, "|")
479 if len(parts) < 1 {
480 return nil, errf("empty query")
481 }
482 cmd := parts[0]
483 parts = parts[1:]
484 stmt := &fakeStmt{q: query, c: c, cmd: cmd}
Brad Fitzpatrick1c441e22012-01-13 15:25:07 -0800485 c.incrStat(&c.stmtsMade)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700486 switch cmd {
487 case "WIPE":
488 // Nothing
489 case "SELECT":
490 return c.prepareSelect(stmt, parts)
491 case "CREATE":
492 return c.prepareCreate(stmt, parts)
493 case "INSERT":
494 return c.prepareInsert(stmt, parts)
Tad Glines41c5d8d2013-08-30 09:27:33 -0700495 case "NOSERT":
496 // Do all the prep-work like for an INSERT but don't actually insert the row.
497 // Used for some of the concurrent tests.
498 return c.prepareInsert(stmt, parts)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700499 default:
Gwenael Treguierc3954dd52012-03-10 15:21:44 -0800500 stmt.Close()
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700501 return nil, errf("unsupported command type %q", cmd)
502 }
503 return stmt, nil
504}
505
506func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
Brad Fitzpatrick93fe8c0c2012-05-29 11:09:09 -0700507 if len(s.placeholderConverter) == 0 {
508 return driver.DefaultParameterConverter
509 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700510 return s.placeholderConverter[idx]
511}
512
Russ Coxc2049d22011-11-01 22:04:37 -0400513func (s *fakeStmt) Close() error {
Brad Fitzpatrick36d3bef2013-04-15 14:06:41 -0700514 if s.c == nil {
515 panic("nil conn in fakeStmt.Close")
516 }
517 if s.c.db == nil {
Brad Fitzpatrick277047f2013-04-25 14:45:56 -0700518 panic("in fakeStmt.Close, conn's db is nil (already closed)")
Brad Fitzpatrick36d3bef2013-04-15 14:06:41 -0700519 }
Brad Fitzpatrick1c441e22012-01-13 15:25:07 -0800520 if !s.closed {
521 s.c.incrStat(&s.c.stmtsClosed)
522 s.closed = true
523 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700524 return nil
525}
526
Brad Fitzpatrick750d0e32011-11-20 14:56:49 -0500527var errClosed = errors.New("fakedb: statement has been closed")
528
Julien Schmidt762a9d92013-12-17 11:57:30 -0800529// hook to simulate broken connections
530var hookExecBadConn func() bool
531
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100532func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
Brad Fitzpatrick750d0e32011-11-20 14:56:49 -0500533 if s.closed {
534 return nil, errClosed
535 }
Julien Schmidt762a9d92013-12-17 11:57:30 -0800536
Marko Tiikkajac468f9462015-03-27 19:45:12 +0100537 if s.c.stickyBad || (hookExecBadConn != nil && hookExecBadConn()) {
Julien Schmidt762a9d92013-12-17 11:57:30 -0800538 return nil, driver.ErrBadConn
539 }
540
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800541 err := checkSubsetTypes(args)
542 if err != nil {
543 return nil, err
544 }
545
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700546 db := s.c.db
547 switch s.cmd {
548 case "WIPE":
549 db.wipe()
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100550 return driver.ResultNoRows, nil
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700551 case "CREATE":
552 if err := db.createTable(s.table, s.colName, s.colType); err != nil {
553 return nil, err
554 }
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100555 return driver.ResultNoRows, nil
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700556 case "INSERT":
Tad Glines41c5d8d2013-08-30 09:27:33 -0700557 return s.execInsert(args, true)
558 case "NOSERT":
559 // Do all the prep-work like for an INSERT but don't actually insert the row.
560 // Used for some of the concurrent tests.
561 return s.execInsert(args, false)
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700562 }
563 fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
564 return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
565}
566
Tad Glines41c5d8d2013-08-30 09:27:33 -0700567// When doInsert is true, add the row to the table.
568// When doInsert is false do prep-work and error checking, but don't
569// actually add the row to the table.
570func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result, error) {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700571 db := s.c.db
572 if len(args) != s.placeholders {
573 panic("error in pkg db; should only get here if size is correct")
574 }
575 db.mu.Lock()
576 t, ok := db.table(s.table)
577 db.mu.Unlock()
578 if !ok {
579 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
580 }
581
582 t.mu.Lock()
583 defer t.mu.Unlock()
584
Tad Glines41c5d8d2013-08-30 09:27:33 -0700585 var cols []interface{}
586 if doInsert {
587 cols = make([]interface{}, len(t.colname))
588 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700589 argPos := 0
590 for n, colname := range s.colName {
591 colidx := t.columnIndex(colname)
592 if colidx == -1 {
593 return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
594 }
595 var val interface{}
596 if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
597 val = args[argPos]
598 argPos++
599 } else {
600 val = s.colValue[n]
601 }
Tad Glines41c5d8d2013-08-30 09:27:33 -0700602 if doInsert {
603 cols[colidx] = val
604 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700605 }
606
Tad Glines41c5d8d2013-08-30 09:27:33 -0700607 if doInsert {
608 t.rows = append(t.rows, &row{cols: cols})
609 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700610 return driver.RowsAffected(1), nil
611}
612
Julien Schmidt762a9d92013-12-17 11:57:30 -0800613// hook to simulate broken connections
614var hookQueryBadConn func() bool
615
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100616func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
Brad Fitzpatrick750d0e32011-11-20 14:56:49 -0500617 if s.closed {
618 return nil, errClosed
619 }
Julien Schmidt762a9d92013-12-17 11:57:30 -0800620
Marko Tiikkajac468f9462015-03-27 19:45:12 +0100621 if s.c.stickyBad || (hookQueryBadConn != nil && hookQueryBadConn()) {
Julien Schmidt762a9d92013-12-17 11:57:30 -0800622 return nil, driver.ErrBadConn
623 }
624
Brad Fitzpatrick0a8005c2011-11-14 10:48:26 -0800625 err := checkSubsetTypes(args)
626 if err != nil {
627 return nil, err
628 }
629
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700630 db := s.c.db
631 if len(args) != s.placeholders {
632 panic("error in pkg db; should only get here if size is correct")
633 }
634
635 db.mu.Lock()
636 t, ok := db.table(s.table)
637 db.mu.Unlock()
638 if !ok {
639 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
640 }
Brad Fitzpatrick277047f2013-04-25 14:45:56 -0700641
642 if s.table == "magicquery" {
643 if len(s.whereCol) == 2 && s.whereCol[0] == "op" && s.whereCol[1] == "millis" {
644 if args[0] == "sleep" {
645 time.Sleep(time.Duration(args[1].(int64)) * time.Millisecond)
646 }
647 }
648 }
649
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700650 t.mu.Lock()
651 defer t.mu.Unlock()
652
653 colIdx := make(map[string]int) // select column name -> column index in table
654 for _, name := range s.colName {
655 idx := t.columnIndex(name)
656 if idx == -1 {
657 return nil, fmt.Errorf("fakedb: unknown column name %q", name)
658 }
659 colIdx[name] = idx
660 }
661
662 mrows := []*row{}
663rows:
664 for _, trow := range t.rows {
665 // Process the where clause, skipping non-match rows. This is lazy
666 // and just uses fmt.Sprintf("%v") to test equality. Good enough
667 // for test code.
668 for widx, wcol := range s.whereCol {
669 idx := t.columnIndex(wcol)
670 if idx == -1 {
671 return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
672 }
673 tcol := trow.cols[idx]
674 if bs, ok := tcol.([]byte); ok {
675 // lazy hack to avoid sprintf %v on a []byte
676 tcol = string(bs)
677 }
678 if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
679 continue rows
680 }
681 }
682 mrow := &row{cols: make([]interface{}, len(s.colName))}
683 for seli, name := range s.colName {
684 mrow.cols[seli] = trow.cols[colIdx[name]]
685 }
686 mrows = append(mrows, mrow)
687 }
688
689 cursor := &rowsCursor{
Nigel Taobc212652013-08-16 11:23:35 +1000690 pos: -1,
691 rows: mrows,
692 cols: s.colName,
693 errPos: -1,
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700694 }
695 return cursor, nil
696}
697
698func (s *fakeStmt) NumInput() int {
699 return s.placeholders
700}
701
Chris Hinesd7376392015-08-24 21:48:39 -0400702// hook to simulate broken connections
703var hookCommitBadConn func() bool
704
Russ Coxc2049d22011-11-01 22:04:37 -0400705func (tx *fakeTx) Commit() error {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700706 tx.c.currTx = nil
Chris Hinesd7376392015-08-24 21:48:39 -0400707 if hookCommitBadConn != nil && hookCommitBadConn() {
708 return driver.ErrBadConn
709 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700710 return nil
711}
712
Chris Hinesd7376392015-08-24 21:48:39 -0400713// hook to simulate broken connections
714var hookRollbackBadConn func() bool
715
Russ Coxc2049d22011-11-01 22:04:37 -0400716func (tx *fakeTx) Rollback() error {
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700717 tx.c.currTx = nil
Chris Hinesd7376392015-08-24 21:48:39 -0400718 if hookRollbackBadConn != nil && hookRollbackBadConn() {
719 return driver.ErrBadConn
720 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700721 return nil
722}
723
724type rowsCursor struct {
725 cols []string
726 pos int
727 rows []*row
728 closed bool
Brad Fitzpatrick701f70a2012-01-12 11:23:33 -0800729
Nigel Taobc212652013-08-16 11:23:35 +1000730 // errPos and err are for making Next return early with error.
731 errPos int
732 err error
733
Brad Fitzpatrick701f70a2012-01-12 11:23:33 -0800734 // a clone of slices to give out to clients, indexed by the
735 // the original slice's first byte address. we clone them
736 // just so we're able to corrupt them on close.
737 bytesClone map[*byte][]byte
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700738}
739
Russ Coxc2049d22011-11-01 22:04:37 -0400740func (rc *rowsCursor) Close() error {
Brad Fitzpatrick701f70a2012-01-12 11:23:33 -0800741 if !rc.closed {
742 for _, bs := range rc.bytesClone {
743 bs[0] = 255 // first byte corrupted
744 }
745 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700746 rc.closed = true
747 return nil
748}
749
750func (rc *rowsCursor) Columns() []string {
751 return rc.cols
752}
753
Marko Tiikkaja1f20ab12013-12-16 12:48:35 -0800754var rowsCursorNextHook func(dest []driver.Value) error
755
Brad Fitzpatrick943f6cc2012-02-20 14:25:28 +1100756func (rc *rowsCursor) Next(dest []driver.Value) error {
Marko Tiikkaja1f20ab12013-12-16 12:48:35 -0800757 if rowsCursorNextHook != nil {
758 return rowsCursorNextHook(dest)
759 }
760
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700761 if rc.closed {
Russ Coxc2049d22011-11-01 22:04:37 -0400762 return errors.New("fakedb: cursor is closed")
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700763 }
764 rc.pos++
Nigel Taobc212652013-08-16 11:23:35 +1000765 if rc.pos == rc.errPos {
766 return rc.err
767 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700768 if rc.pos >= len(rc.rows) {
Russ Coxc2049d22011-11-01 22:04:37 -0400769 return io.EOF // per interface spec
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700770 }
771 for i, v := range rc.rows[rc.pos].cols {
772 // TODO(bradfitz): convert to subset types? naah, I
773 // think the subset types should only be input to
Brad Fitzpatrick8089e572011-11-02 11:46:04 -0700774 // driver, but the sql package should be able to handle
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700775 // a wider range of types coming out of drivers. all
776 // for ease of drivers, and to prevent drivers from
777 // messing up conversions or doing them differently.
778 dest[i] = v
Brad Fitzpatrick701f70a2012-01-12 11:23:33 -0800779
780 if bs, ok := v.([]byte); ok {
781 if rc.bytesClone == nil {
782 rc.bytesClone = make(map[*byte][]byte)
783 }
784 clone, ok := rc.bytesClone[&bs[0]]
785 if !ok {
786 clone = make([]byte, len(bs))
787 copy(clone, bs)
788 rc.bytesClone[&bs[0]] = clone
789 }
790 dest[i] = clone
791 }
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700792 }
793 return nil
794}
795
Brad Fitzpatrick93fe8c0c2012-05-29 11:09:09 -0700796// fakeDriverString is like driver.String, but indirects pointers like
797// DefaultValueConverter.
798//
799// This could be surprising behavior to retroactively apply to
800// driver.String now that Go1 is out, but this is convenient for
801// our TestPointerParamsAndScans.
802//
803type fakeDriverString struct{}
804
805func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
806 switch c := v.(type) {
807 case string, []byte:
808 return v, nil
809 case *string:
810 if c == nil {
811 return nil, nil
812 }
813 return *c, nil
814 }
815 return fmt.Sprintf("%v", v), nil
816}
817
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700818func converterForType(typ string) driver.ValueConverter {
819 switch typ {
820 case "bool":
821 return driver.Bool
James P. Cooperc21b3432012-01-25 17:47:32 -0800822 case "nullbool":
Nigel Tao102638c2012-02-03 10:12:25 +1100823 return driver.Null{Converter: driver.Bool}
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700824 case "int32":
825 return driver.Int32
826 case "string":
Brad Fitzpatrick93fe8c0c2012-05-29 11:09:09 -0700827 return driver.NotNull{Converter: fakeDriverString{}}
Brad Fitzpatrickbc0139b2012-01-19 09:27:45 -0800828 case "nullstring":
Brad Fitzpatrick93fe8c0c2012-05-29 11:09:09 -0700829 return driver.Null{Converter: fakeDriverString{}}
James P. Cooperc21b3432012-01-25 17:47:32 -0800830 case "int64":
831 // TODO(coopernurse): add type-specific converter
Nigel Tao102638c2012-02-03 10:12:25 +1100832 return driver.NotNull{Converter: driver.DefaultParameterConverter}
James P. Cooperc21b3432012-01-25 17:47:32 -0800833 case "nullint64":
834 // TODO(coopernurse): add type-specific converter
Nigel Tao102638c2012-02-03 10:12:25 +1100835 return driver.Null{Converter: driver.DefaultParameterConverter}
James P. Cooperc21b3432012-01-25 17:47:32 -0800836 case "float64":
837 // TODO(coopernurse): add type-specific converter
Nigel Tao102638c2012-02-03 10:12:25 +1100838 return driver.NotNull{Converter: driver.DefaultParameterConverter}
James P. Cooperc21b3432012-01-25 17:47:32 -0800839 case "nullfloat64":
840 // TODO(coopernurse): add type-specific converter
Nigel Tao102638c2012-02-03 10:12:25 +1100841 return driver.Null{Converter: driver.DefaultParameterConverter}
Brad Fitzpatrickbf734d62012-01-13 15:45:05 -0800842 case "datetime":
843 return driver.DefaultParameterConverter
Brad Fitzpatrick357f2cb2011-09-29 16:12:21 -0700844 }
845 panic("invalid fakedb column type of " + typ)
846}