1// 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 (
8	"database/sql/driver"
9	"errors"
10	"fmt"
11	"io"
12	"log"
13	"sort"
14	"strconv"
15	"strings"
16	"sync"
17	"testing"
18	"time"
19)
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
27// syntactically different and simpler than SQL.  The syntax is as
28// 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//
36// Any of these can be preceded by PANIC|<method>|, to cause the
37// named method on fakeStmt to panic.
38//
39// When opening a fakeDriver's database, it starts empty with no
40// tables.  All tables and data are stored in memory only.
41type fakeDriver struct {
42	mu         sync.Mutex // guards 3 following fields
43	openCount  int        // conn opens
44	closeCount int        // conn closes
45	waitCh     chan struct{}
46	waitingCh  chan struct{}
47	dbs        map[string]*fakeDB
48}
49
50type fakeDB struct {
51	name string
52
53	mu      sync.Mutex
54	free    []*fakeConn
55	tables  map[string]*table
56	badConn bool
57}
58
59type table struct {
60	mu      sync.Mutex
61	colname []string
62	coltype []string
63	rows    []*row
64}
65
66func (t *table) columnIndex(name string) int {
67	for n, nname := range t.colname {
68		if name == nname {
69			return n
70		}
71	}
72	return -1
73}
74
75type row struct {
76	cols []interface{} // must be same size as its table colname + coltype
77}
78
79func (r *row) clone() *row {
80	nrow := &row{cols: make([]interface{}, len(r.cols))}
81	copy(nrow.cols, r.cols)
82	return nrow
83}
84
85type fakeConn struct {
86	db *fakeDB // where to return ourselves to
87
88	currTx *fakeTx
89
90	// Stats for tests:
91	mu          sync.Mutex
92	stmtsMade   int
93	stmtsClosed int
94	numPrepare  int
95
96	// bad connection tests; see isBad()
97	bad       bool
98	stickyBad bool
99}
100
101func (c *fakeConn) incrStat(v *int) {
102	c.mu.Lock()
103	*v++
104	c.mu.Unlock()
105}
106
107type fakeTx struct {
108	c *fakeConn
109}
110
111type fakeStmt struct {
112	c *fakeConn
113	q string // just for debugging
114
115	cmd   string
116	table string
117	panic string
118
119	closed bool
120
121	colName      []string      // used by CREATE, INSERT, SELECT (selected columns)
122	colType      []string      // used by CREATE
123	colValue     []interface{} // used by INSERT (mix of strings and "?" for bound params)
124	placeholders int           // used by INSERT/SELECT: number of ? params
125
126	whereCol []string // used by SELECT (all placeholders)
127
128	placeholderConverter []driver.ValueConverter // used by INSERT
129}
130
131var fdriver driver.Driver = &fakeDriver{}
132
133func init() {
134	Register("test", fdriver)
135}
136
137func contains(list []string, y string) bool {
138	for _, x := range list {
139		if x == y {
140			return true
141		}
142	}
143	return false
144}
145
146type Dummy struct {
147	driver.Driver
148}
149
150func TestDrivers(t *testing.T) {
151	unregisterAllDrivers()
152	Register("test", fdriver)
153	Register("invalid", Dummy{})
154	all := Drivers()
155	if len(all) < 2 || !sort.StringsAreSorted(all) || !contains(all, "test") || !contains(all, "invalid") {
156		t.Fatalf("Drivers = %v, want sorted list with at least [invalid, test]", all)
157	}
158}
159
160// hook to simulate connection failures
161var hookOpenErr struct {
162	sync.Mutex
163	fn func() error
164}
165
166func setHookOpenErr(fn func() error) {
167	hookOpenErr.Lock()
168	defer hookOpenErr.Unlock()
169	hookOpenErr.fn = fn
170}
171
172// Supports dsn forms:
173//    <dbname>
174//    <dbname>;<opts>  (only currently supported option is `badConn`,
175//                      which causes driver.ErrBadConn to be returned on
176//                      every other conn.Begin())
177func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
178	hookOpenErr.Lock()
179	fn := hookOpenErr.fn
180	hookOpenErr.Unlock()
181	if fn != nil {
182		if err := fn(); err != nil {
183			return nil, err
184		}
185	}
186	parts := strings.Split(dsn, ";")
187	if len(parts) < 1 {
188		return nil, errors.New("fakedb: no database name")
189	}
190	name := parts[0]
191
192	db := d.getDB(name)
193
194	d.mu.Lock()
195	d.openCount++
196	d.mu.Unlock()
197	conn := &fakeConn{db: db}
198
199	if len(parts) >= 2 && parts[1] == "badConn" {
200		conn.bad = true
201	}
202	if d.waitCh != nil {
203		d.waitingCh <- struct{}{}
204		<-d.waitCh
205		d.waitCh = nil
206		d.waitingCh = nil
207	}
208	return conn, nil
209}
210
211func (d *fakeDriver) getDB(name string) *fakeDB {
212	d.mu.Lock()
213	defer d.mu.Unlock()
214	if d.dbs == nil {
215		d.dbs = make(map[string]*fakeDB)
216	}
217	db, ok := d.dbs[name]
218	if !ok {
219		db = &fakeDB{name: name}
220		d.dbs[name] = db
221	}
222	return db
223}
224
225func (db *fakeDB) wipe() {
226	db.mu.Lock()
227	defer db.mu.Unlock()
228	db.tables = nil
229}
230
231func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
232	db.mu.Lock()
233	defer db.mu.Unlock()
234	if db.tables == nil {
235		db.tables = make(map[string]*table)
236	}
237	if _, exist := db.tables[name]; exist {
238		return fmt.Errorf("table %q already exists", name)
239	}
240	if len(columnNames) != len(columnTypes) {
241		return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d",
242			name, len(columnNames), len(columnTypes))
243	}
244	db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
245	return nil
246}
247
248// must be called with db.mu lock held
249func (db *fakeDB) table(table string) (*table, bool) {
250	if db.tables == nil {
251		return nil, false
252	}
253	t, ok := db.tables[table]
254	return t, ok
255}
256
257func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
258	db.mu.Lock()
259	defer db.mu.Unlock()
260	t, ok := db.table(table)
261	if !ok {
262		return
263	}
264	for n, cname := range t.colname {
265		if cname == column {
266			return t.coltype[n], true
267		}
268	}
269	return "", false
270}
271
272func (c *fakeConn) isBad() bool {
273	if c.stickyBad {
274		return true
275	} else if c.bad {
276		// alternate between bad conn and not bad conn
277		c.db.badConn = !c.db.badConn
278		return c.db.badConn
279	} else {
280		return false
281	}
282}
283
284func (c *fakeConn) Begin() (driver.Tx, error) {
285	if c.isBad() {
286		return nil, driver.ErrBadConn
287	}
288	if c.currTx != nil {
289		return nil, errors.New("already in a transaction")
290	}
291	c.currTx = &fakeTx{c: c}
292	return c.currTx, nil
293}
294
295var hookPostCloseConn struct {
296	sync.Mutex
297	fn func(*fakeConn, error)
298}
299
300func setHookpostCloseConn(fn func(*fakeConn, error)) {
301	hookPostCloseConn.Lock()
302	defer hookPostCloseConn.Unlock()
303	hookPostCloseConn.fn = fn
304}
305
306var testStrictClose *testing.T
307
308// setStrictFakeConnClose sets the t to Errorf on when fakeConn.Close
309// fails to close. If nil, the check is disabled.
310func setStrictFakeConnClose(t *testing.T) {
311	testStrictClose = t
312}
313
314func (c *fakeConn) Close() (err error) {
315	drv := fdriver.(*fakeDriver)
316	defer func() {
317		if err != nil && testStrictClose != nil {
318			testStrictClose.Errorf("failed to close a test fakeConn: %v", err)
319		}
320		hookPostCloseConn.Lock()
321		fn := hookPostCloseConn.fn
322		hookPostCloseConn.Unlock()
323		if fn != nil {
324			fn(c, err)
325		}
326		if err == nil {
327			drv.mu.Lock()
328			drv.closeCount++
329			drv.mu.Unlock()
330		}
331	}()
332	if c.currTx != nil {
333		return errors.New("can't close fakeConn; in a Transaction")
334	}
335	if c.db == nil {
336		return errors.New("can't close fakeConn; already closed")
337	}
338	if c.stmtsMade > c.stmtsClosed {
339		return errors.New("can't close; dangling statement(s)")
340	}
341	c.db = nil
342	return nil
343}
344
345func checkSubsetTypes(args []driver.Value) error {
346	for n, arg := range args {
347		switch arg.(type) {
348		case int64, float64, bool, nil, []byte, string, time.Time:
349		default:
350			return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
351		}
352	}
353	return nil
354}
355
356func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
357	// This is an optional interface, but it's implemented here
358	// just to check that all the args are of the proper types.
359	// ErrSkip is returned so the caller acts as if we didn't
360	// implement this at all.
361	err := checkSubsetTypes(args)
362	if err != nil {
363		return nil, err
364	}
365	return nil, driver.ErrSkip
366}
367
368func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
369	// This is an optional interface, but it's implemented here
370	// just to check that all the args are of the proper types.
371	// ErrSkip is returned so the caller acts as if we didn't
372	// implement this at all.
373	err := checkSubsetTypes(args)
374	if err != nil {
375		return nil, err
376	}
377	return nil, driver.ErrSkip
378}
379
380func errf(msg string, args ...interface{}) error {
381	return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
382}
383
384// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
385// (note that where columns must always contain ? marks,
386//  just a limitation for fakedb)
387func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
388	if len(parts) != 3 {
389		stmt.Close()
390		return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
391	}
392	stmt.table = parts[0]
393	stmt.colName = strings.Split(parts[1], ",")
394	for n, colspec := range strings.Split(parts[2], ",") {
395		if colspec == "" {
396			continue
397		}
398		nameVal := strings.Split(colspec, "=")
399		if len(nameVal) != 2 {
400			stmt.Close()
401			return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
402		}
403		column, value := nameVal[0], nameVal[1]
404		_, ok := c.db.columnType(stmt.table, column)
405		if !ok {
406			stmt.Close()
407			return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
408		}
409		if value != "?" {
410			stmt.Close()
411			return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
412				stmt.table, column)
413		}
414		stmt.whereCol = append(stmt.whereCol, column)
415		stmt.placeholders++
416	}
417	return stmt, nil
418}
419
420// parts are table|col=type,col2=type2
421func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
422	if len(parts) != 2 {
423		stmt.Close()
424		return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
425	}
426	stmt.table = parts[0]
427	for n, colspec := range strings.Split(parts[1], ",") {
428		nameType := strings.Split(colspec, "=")
429		if len(nameType) != 2 {
430			stmt.Close()
431			return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
432		}
433		stmt.colName = append(stmt.colName, nameType[0])
434		stmt.colType = append(stmt.colType, nameType[1])
435	}
436	return stmt, nil
437}
438
439// parts are table|col=?,col2=val
440func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
441	if len(parts) != 2 {
442		stmt.Close()
443		return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
444	}
445	stmt.table = parts[0]
446	for n, colspec := range strings.Split(parts[1], ",") {
447		nameVal := strings.Split(colspec, "=")
448		if len(nameVal) != 2 {
449			stmt.Close()
450			return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
451		}
452		column, value := nameVal[0], nameVal[1]
453		ctype, ok := c.db.columnType(stmt.table, column)
454		if !ok {
455			stmt.Close()
456			return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
457		}
458		stmt.colName = append(stmt.colName, column)
459
460		if value != "?" {
461			var subsetVal interface{}
462			// Convert to driver subset type
463			switch ctype {
464			case "string":
465				subsetVal = []byte(value)
466			case "blob":
467				subsetVal = []byte(value)
468			case "int32":
469				i, err := strconv.Atoi(value)
470				if err != nil {
471					stmt.Close()
472					return nil, errf("invalid conversion to int32 from %q", value)
473				}
474				subsetVal = int64(i) // int64 is a subset type, but not int32
475			default:
476				stmt.Close()
477				return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
478			}
479			stmt.colValue = append(stmt.colValue, subsetVal)
480		} else {
481			stmt.placeholders++
482			stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
483			stmt.colValue = append(stmt.colValue, "?")
484		}
485	}
486	return stmt, nil
487}
488
489// hook to simulate broken connections
490var hookPrepareBadConn func() bool
491
492func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
493	c.numPrepare++
494	if c.db == nil {
495		panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
496	}
497
498	if c.stickyBad || (hookPrepareBadConn != nil && hookPrepareBadConn()) {
499		return nil, driver.ErrBadConn
500	}
501
502	parts := strings.Split(query, "|")
503	if len(parts) < 1 {
504		return nil, errf("empty query")
505	}
506	stmt := &fakeStmt{q: query, c: c}
507	if len(parts) >= 3 && parts[0] == "PANIC" {
508		stmt.panic = parts[1]
509		parts = parts[2:]
510	}
511	cmd := parts[0]
512	stmt.cmd = cmd
513	parts = parts[1:]
514
515	c.incrStat(&c.stmtsMade)
516	switch cmd {
517	case "WIPE":
518		// Nothing
519	case "SELECT":
520		return c.prepareSelect(stmt, parts)
521	case "CREATE":
522		return c.prepareCreate(stmt, parts)
523	case "INSERT":
524		return c.prepareInsert(stmt, parts)
525	case "NOSERT":
526		// Do all the prep-work like for an INSERT but don't actually insert the row.
527		// Used for some of the concurrent tests.
528		return c.prepareInsert(stmt, parts)
529	default:
530		stmt.Close()
531		return nil, errf("unsupported command type %q", cmd)
532	}
533	return stmt, nil
534}
535
536func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
537	if s.panic == "ColumnConverter" {
538		panic(s.panic)
539	}
540	if len(s.placeholderConverter) == 0 {
541		return driver.DefaultParameterConverter
542	}
543	return s.placeholderConverter[idx]
544}
545
546func (s *fakeStmt) Close() error {
547	if s.panic == "Close" {
548		panic(s.panic)
549	}
550	if s.c == nil {
551		panic("nil conn in fakeStmt.Close")
552	}
553	if s.c.db == nil {
554		panic("in fakeStmt.Close, conn's db is nil (already closed)")
555	}
556	if !s.closed {
557		s.c.incrStat(&s.c.stmtsClosed)
558		s.closed = true
559	}
560	return nil
561}
562
563var errClosed = errors.New("fakedb: statement has been closed")
564
565// hook to simulate broken connections
566var hookExecBadConn func() bool
567
568func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
569	if s.panic == "Exec" {
570		panic(s.panic)
571	}
572	if s.closed {
573		return nil, errClosed
574	}
575
576	if s.c.stickyBad || (hookExecBadConn != nil && hookExecBadConn()) {
577		return nil, driver.ErrBadConn
578	}
579
580	err := checkSubsetTypes(args)
581	if err != nil {
582		return nil, err
583	}
584
585	db := s.c.db
586	switch s.cmd {
587	case "WIPE":
588		db.wipe()
589		return driver.ResultNoRows, nil
590	case "CREATE":
591		if err := db.createTable(s.table, s.colName, s.colType); err != nil {
592			return nil, err
593		}
594		return driver.ResultNoRows, nil
595	case "INSERT":
596		return s.execInsert(args, true)
597	case "NOSERT":
598		// Do all the prep-work like for an INSERT but don't actually insert the row.
599		// Used for some of the concurrent tests.
600		return s.execInsert(args, false)
601	}
602	fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
603	return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
604}
605
606// When doInsert is true, add the row to the table.
607// When doInsert is false do prep-work and error checking, but don't
608// actually add the row to the table.
609func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result, error) {
610	db := s.c.db
611	if len(args) != s.placeholders {
612		panic("error in pkg db; should only get here if size is correct")
613	}
614	db.mu.Lock()
615	t, ok := db.table(s.table)
616	db.mu.Unlock()
617	if !ok {
618		return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
619	}
620
621	t.mu.Lock()
622	defer t.mu.Unlock()
623
624	var cols []interface{}
625	if doInsert {
626		cols = make([]interface{}, len(t.colname))
627	}
628	argPos := 0
629	for n, colname := range s.colName {
630		colidx := t.columnIndex(colname)
631		if colidx == -1 {
632			return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
633		}
634		var val interface{}
635		if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
636			val = args[argPos]
637			argPos++
638		} else {
639			val = s.colValue[n]
640		}
641		if doInsert {
642			cols[colidx] = val
643		}
644	}
645
646	if doInsert {
647		t.rows = append(t.rows, &row{cols: cols})
648	}
649	return driver.RowsAffected(1), nil
650}
651
652// hook to simulate broken connections
653var hookQueryBadConn func() bool
654
655func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
656	if s.panic == "Query" {
657		panic(s.panic)
658	}
659	if s.closed {
660		return nil, errClosed
661	}
662
663	if s.c.stickyBad || (hookQueryBadConn != nil && hookQueryBadConn()) {
664		return nil, driver.ErrBadConn
665	}
666
667	err := checkSubsetTypes(args)
668	if err != nil {
669		return nil, err
670	}
671
672	db := s.c.db
673	if len(args) != s.placeholders {
674		panic("error in pkg db; should only get here if size is correct")
675	}
676
677	db.mu.Lock()
678	t, ok := db.table(s.table)
679	db.mu.Unlock()
680	if !ok {
681		return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
682	}
683
684	if s.table == "magicquery" {
685		if len(s.whereCol) == 2 && s.whereCol[0] == "op" && s.whereCol[1] == "millis" {
686			if args[0] == "sleep" {
687				time.Sleep(time.Duration(args[1].(int64)) * time.Millisecond)
688			}
689		}
690	}
691
692	t.mu.Lock()
693	defer t.mu.Unlock()
694
695	colIdx := make(map[string]int) // select column name -> column index in table
696	for _, name := range s.colName {
697		idx := t.columnIndex(name)
698		if idx == -1 {
699			return nil, fmt.Errorf("fakedb: unknown column name %q", name)
700		}
701		colIdx[name] = idx
702	}
703
704	mrows := []*row{}
705rows:
706	for _, trow := range t.rows {
707		// Process the where clause, skipping non-match rows. This is lazy
708		// and just uses fmt.Sprintf("%v") to test equality.  Good enough
709		// for test code.
710		for widx, wcol := range s.whereCol {
711			idx := t.columnIndex(wcol)
712			if idx == -1 {
713				return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
714			}
715			tcol := trow.cols[idx]
716			if bs, ok := tcol.([]byte); ok {
717				// lazy hack to avoid sprintf %v on a []byte
718				tcol = string(bs)
719			}
720			if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
721				continue rows
722			}
723		}
724		mrow := &row{cols: make([]interface{}, len(s.colName))}
725		for seli, name := range s.colName {
726			mrow.cols[seli] = trow.cols[colIdx[name]]
727		}
728		mrows = append(mrows, mrow)
729	}
730
731	cursor := &rowsCursor{
732		pos:    -1,
733		rows:   mrows,
734		cols:   s.colName,
735		errPos: -1,
736	}
737	return cursor, nil
738}
739
740func (s *fakeStmt) NumInput() int {
741	if s.panic == "NumInput" {
742		panic(s.panic)
743	}
744	return s.placeholders
745}
746
747// hook to simulate broken connections
748var hookCommitBadConn func() bool
749
750func (tx *fakeTx) Commit() error {
751	tx.c.currTx = nil
752	if hookCommitBadConn != nil && hookCommitBadConn() {
753		return driver.ErrBadConn
754	}
755	return nil
756}
757
758// hook to simulate broken connections
759var hookRollbackBadConn func() bool
760
761func (tx *fakeTx) Rollback() error {
762	tx.c.currTx = nil
763	if hookRollbackBadConn != nil && hookRollbackBadConn() {
764		return driver.ErrBadConn
765	}
766	return nil
767}
768
769type rowsCursor struct {
770	cols   []string
771	pos    int
772	rows   []*row
773	closed bool
774
775	// errPos and err are for making Next return early with error.
776	errPos int
777	err    error
778
779	// a clone of slices to give out to clients, indexed by the
780	// the original slice's first byte address.  we clone them
781	// just so we're able to corrupt them on close.
782	bytesClone map[*byte][]byte
783}
784
785func (rc *rowsCursor) Close() error {
786	if !rc.closed {
787		for _, bs := range rc.bytesClone {
788			bs[0] = 255 // first byte corrupted
789		}
790	}
791	rc.closed = true
792	return nil
793}
794
795func (rc *rowsCursor) Columns() []string {
796	return rc.cols
797}
798
799var rowsCursorNextHook func(dest []driver.Value) error
800
801func (rc *rowsCursor) Next(dest []driver.Value) error {
802	if rowsCursorNextHook != nil {
803		return rowsCursorNextHook(dest)
804	}
805
806	if rc.closed {
807		return errors.New("fakedb: cursor is closed")
808	}
809	rc.pos++
810	if rc.pos == rc.errPos {
811		return rc.err
812	}
813	if rc.pos >= len(rc.rows) {
814		return io.EOF // per interface spec
815	}
816	for i, v := range rc.rows[rc.pos].cols {
817		// TODO(bradfitz): convert to subset types? naah, I
818		// think the subset types should only be input to
819		// driver, but the sql package should be able to handle
820		// a wider range of types coming out of drivers. all
821		// for ease of drivers, and to prevent drivers from
822		// messing up conversions or doing them differently.
823		dest[i] = v
824
825		if bs, ok := v.([]byte); ok {
826			if rc.bytesClone == nil {
827				rc.bytesClone = make(map[*byte][]byte)
828			}
829			clone, ok := rc.bytesClone[&bs[0]]
830			if !ok {
831				clone = make([]byte, len(bs))
832				copy(clone, bs)
833				rc.bytesClone[&bs[0]] = clone
834			}
835			dest[i] = clone
836		}
837	}
838	return nil
839}
840
841// fakeDriverString is like driver.String, but indirects pointers like
842// DefaultValueConverter.
843//
844// This could be surprising behavior to retroactively apply to
845// driver.String now that Go1 is out, but this is convenient for
846// our TestPointerParamsAndScans.
847//
848type fakeDriverString struct{}
849
850func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
851	switch c := v.(type) {
852	case string, []byte:
853		return v, nil
854	case *string:
855		if c == nil {
856			return nil, nil
857		}
858		return *c, nil
859	}
860	return fmt.Sprintf("%v", v), nil
861}
862
863func converterForType(typ string) driver.ValueConverter {
864	switch typ {
865	case "bool":
866		return driver.Bool
867	case "nullbool":
868		return driver.Null{Converter: driver.Bool}
869	case "int32":
870		return driver.Int32
871	case "string":
872		return driver.NotNull{Converter: fakeDriverString{}}
873	case "nullstring":
874		return driver.Null{Converter: fakeDriverString{}}
875	case "int64":
876		// TODO(coopernurse): add type-specific converter
877		return driver.NotNull{Converter: driver.DefaultParameterConverter}
878	case "nullint64":
879		// TODO(coopernurse): add type-specific converter
880		return driver.Null{Converter: driver.DefaultParameterConverter}
881	case "float64":
882		// TODO(coopernurse): add type-specific converter
883		return driver.NotNull{Converter: driver.DefaultParameterConverter}
884	case "nullfloat64":
885		// TODO(coopernurse): add type-specific converter
886		return driver.Null{Converter: driver.DefaultParameterConverter}
887	case "datetime":
888		return driver.DefaultParameterConverter
889	}
890	panic("invalid fakedb column type of " + typ)
891}
892