github.com/zach-klippenstein/go@v0.0.0-20150108044943-fcfbeb3adf58/src/database/sql/fakedb_test.go (about)

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