...
Run Format

Source file src/database/sql/fakedb_test.go

Documentation: database/sql

     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  	"context"
     9  	"database/sql/driver"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"reflect"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  var _ = log.Printf
    24  
    25  // fakeDriver is a fake database that implements Go's driver.Driver
    26  // interface, just for testing.
    27  //
    28  // It speaks a query language that's semantically similar to but
    29  // syntactically different and simpler than SQL.  The syntax is as
    30  // follows:
    31  //
    32  //   WIPE
    33  //   CREATE|<tablename>|<col>=<type>,<col>=<type>,...
    34  //     where types are: "string", [u]int{8,16,32,64}, "bool"
    35  //   INSERT|<tablename>|col=val,col2=val2,col3=?
    36  //   SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
    37  //   SELECT|<tablename>|projectcol1,projectcol2|filtercol=?param1,filtercol2=?param2
    38  //
    39  // Any of these can be preceded by PANIC|<method>|, to cause the
    40  // named method on fakeStmt to panic.
    41  //
    42  // Any of these can be proceeded by WAIT|<duration>|, to cause the
    43  // named method on fakeStmt to sleep for the specified duration.
    44  //
    45  // Multiple of these can be combined when separated with a semicolon.
    46  //
    47  // When opening a fakeDriver's database, it starts empty with no
    48  // tables. All tables and data are stored in memory only.
    49  type fakeDriver struct {
    50  	mu         sync.Mutex // guards 3 following fields
    51  	openCount  int        // conn opens
    52  	closeCount int        // conn closes
    53  	waitCh     chan struct{}
    54  	waitingCh  chan struct{}
    55  	dbs        map[string]*fakeDB
    56  }
    57  
    58  type fakeConnector struct {
    59  	name string
    60  
    61  	waiter func(context.Context)
    62  }
    63  
    64  func (c *fakeConnector) Connect(context.Context) (driver.Conn, error) {
    65  	conn, err := fdriver.Open(c.name)
    66  	conn.(*fakeConn).waiter = c.waiter
    67  	return conn, err
    68  }
    69  
    70  func (c *fakeConnector) Driver() driver.Driver {
    71  	return fdriver
    72  }
    73  
    74  type fakeDriverCtx struct {
    75  	fakeDriver
    76  }
    77  
    78  var _ driver.DriverContext = &fakeDriverCtx{}
    79  
    80  func (cc *fakeDriverCtx) OpenConnector(name string) (driver.Connector, error) {
    81  	return &fakeConnector{name: name}, nil
    82  }
    83  
    84  type fakeDB struct {
    85  	name string
    86  
    87  	mu       sync.Mutex
    88  	tables   map[string]*table
    89  	badConn  bool
    90  	allowAny bool
    91  }
    92  
    93  type table struct {
    94  	mu      sync.Mutex
    95  	colname []string
    96  	coltype []string
    97  	rows    []*row
    98  }
    99  
   100  func (t *table) columnIndex(name string) int {
   101  	for n, nname := range t.colname {
   102  		if name == nname {
   103  			return n
   104  		}
   105  	}
   106  	return -1
   107  }
   108  
   109  type row struct {
   110  	cols []interface{} // must be same size as its table colname + coltype
   111  }
   112  
   113  type memToucher interface {
   114  	// touchMem reads & writes some memory, to help find data races.
   115  	touchMem()
   116  }
   117  
   118  type fakeConn struct {
   119  	db *fakeDB // where to return ourselves to
   120  
   121  	currTx *fakeTx
   122  
   123  	// Every operation writes to line to enable the race detector
   124  	// check for data races.
   125  	line int64
   126  
   127  	// Stats for tests:
   128  	mu          sync.Mutex
   129  	stmtsMade   int
   130  	stmtsClosed int
   131  	numPrepare  int
   132  
   133  	// bad connection tests; see isBad()
   134  	bad       bool
   135  	stickyBad bool
   136  
   137  	skipDirtySession bool // tests that use Conn should set this to true.
   138  
   139  	// dirtySession tests ResetSession, true if a query has executed
   140  	// until ResetSession is called.
   141  	dirtySession bool
   142  
   143  	// The waiter is called before each query. May be used in place of the "WAIT"
   144  	// directive.
   145  	waiter func(context.Context)
   146  }
   147  
   148  func (c *fakeConn) touchMem() {
   149  	c.line++
   150  }
   151  
   152  func (c *fakeConn) incrStat(v *int) {
   153  	c.mu.Lock()
   154  	*v++
   155  	c.mu.Unlock()
   156  }
   157  
   158  type fakeTx struct {
   159  	c *fakeConn
   160  }
   161  
   162  type boundCol struct {
   163  	Column      string
   164  	Placeholder string
   165  	Ordinal     int
   166  }
   167  
   168  type fakeStmt struct {
   169  	memToucher
   170  	c *fakeConn
   171  	q string // just for debugging
   172  
   173  	cmd   string
   174  	table string
   175  	panic string
   176  	wait  time.Duration
   177  
   178  	next *fakeStmt // used for returning multiple results.
   179  
   180  	closed bool
   181  
   182  	colName      []string      // used by CREATE, INSERT, SELECT (selected columns)
   183  	colType      []string      // used by CREATE
   184  	colValue     []interface{} // used by INSERT (mix of strings and "?" for bound params)
   185  	placeholders int           // used by INSERT/SELECT: number of ? params
   186  
   187  	whereCol []boundCol // used by SELECT (all placeholders)
   188  
   189  	placeholderConverter []driver.ValueConverter // used by INSERT
   190  }
   191  
   192  var fdriver driver.Driver = &fakeDriver{}
   193  
   194  func init() {
   195  	Register("test", fdriver)
   196  }
   197  
   198  func contains(list []string, y string) bool {
   199  	for _, x := range list {
   200  		if x == y {
   201  			return true
   202  		}
   203  	}
   204  	return false
   205  }
   206  
   207  type Dummy struct {
   208  	driver.Driver
   209  }
   210  
   211  func TestDrivers(t *testing.T) {
   212  	unregisterAllDrivers()
   213  	Register("test", fdriver)
   214  	Register("invalid", Dummy{})
   215  	all := Drivers()
   216  	if len(all) < 2 || !sort.StringsAreSorted(all) || !contains(all, "test") || !contains(all, "invalid") {
   217  		t.Fatalf("Drivers = %v, want sorted list with at least [invalid, test]", all)
   218  	}
   219  }
   220  
   221  // hook to simulate connection failures
   222  var hookOpenErr struct {
   223  	sync.Mutex
   224  	fn func() error
   225  }
   226  
   227  func setHookOpenErr(fn func() error) {
   228  	hookOpenErr.Lock()
   229  	defer hookOpenErr.Unlock()
   230  	hookOpenErr.fn = fn
   231  }
   232  
   233  // Supports dsn forms:
   234  //    <dbname>
   235  //    <dbname>;<opts>  (only currently supported option is `badConn`,
   236  //                      which causes driver.ErrBadConn to be returned on
   237  //                      every other conn.Begin())
   238  func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
   239  	hookOpenErr.Lock()
   240  	fn := hookOpenErr.fn
   241  	hookOpenErr.Unlock()
   242  	if fn != nil {
   243  		if err := fn(); err != nil {
   244  			return nil, err
   245  		}
   246  	}
   247  	parts := strings.Split(dsn, ";")
   248  	if len(parts) < 1 {
   249  		return nil, errors.New("fakedb: no database name")
   250  	}
   251  	name := parts[0]
   252  
   253  	db := d.getDB(name)
   254  
   255  	d.mu.Lock()
   256  	d.openCount++
   257  	d.mu.Unlock()
   258  	conn := &fakeConn{db: db}
   259  
   260  	if len(parts) >= 2 && parts[1] == "badConn" {
   261  		conn.bad = true
   262  	}
   263  	if d.waitCh != nil {
   264  		d.waitingCh <- struct{}{}
   265  		<-d.waitCh
   266  		d.waitCh = nil
   267  		d.waitingCh = nil
   268  	}
   269  	return conn, nil
   270  }
   271  
   272  func (d *fakeDriver) getDB(name string) *fakeDB {
   273  	d.mu.Lock()
   274  	defer d.mu.Unlock()
   275  	if d.dbs == nil {
   276  		d.dbs = make(map[string]*fakeDB)
   277  	}
   278  	db, ok := d.dbs[name]
   279  	if !ok {
   280  		db = &fakeDB{name: name}
   281  		d.dbs[name] = db
   282  	}
   283  	return db
   284  }
   285  
   286  func (db *fakeDB) wipe() {
   287  	db.mu.Lock()
   288  	defer db.mu.Unlock()
   289  	db.tables = nil
   290  }
   291  
   292  func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
   293  	db.mu.Lock()
   294  	defer db.mu.Unlock()
   295  	if db.tables == nil {
   296  		db.tables = make(map[string]*table)
   297  	}
   298  	if _, exist := db.tables[name]; exist {
   299  		return fmt.Errorf("fakedb: table %q already exists", name)
   300  	}
   301  	if len(columnNames) != len(columnTypes) {
   302  		return fmt.Errorf("fakedb: create table of %q len(names) != len(types): %d vs %d",
   303  			name, len(columnNames), len(columnTypes))
   304  	}
   305  	db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
   306  	return nil
   307  }
   308  
   309  // must be called with db.mu lock held
   310  func (db *fakeDB) table(table string) (*table, bool) {
   311  	if db.tables == nil {
   312  		return nil, false
   313  	}
   314  	t, ok := db.tables[table]
   315  	return t, ok
   316  }
   317  
   318  func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
   319  	db.mu.Lock()
   320  	defer db.mu.Unlock()
   321  	t, ok := db.table(table)
   322  	if !ok {
   323  		return
   324  	}
   325  	for n, cname := range t.colname {
   326  		if cname == column {
   327  			return t.coltype[n], true
   328  		}
   329  	}
   330  	return "", false
   331  }
   332  
   333  func (c *fakeConn) isBad() bool {
   334  	if c.stickyBad {
   335  		return true
   336  	} else if c.bad {
   337  		if c.db == nil {
   338  			return false
   339  		}
   340  		// alternate between bad conn and not bad conn
   341  		c.db.badConn = !c.db.badConn
   342  		return c.db.badConn
   343  	} else {
   344  		return false
   345  	}
   346  }
   347  
   348  func (c *fakeConn) isDirtyAndMark() bool {
   349  	if c.skipDirtySession {
   350  		return false
   351  	}
   352  	if c.currTx != nil {
   353  		c.dirtySession = true
   354  		return false
   355  	}
   356  	if c.dirtySession {
   357  		return true
   358  	}
   359  	c.dirtySession = true
   360  	return false
   361  }
   362  
   363  func (c *fakeConn) Begin() (driver.Tx, error) {
   364  	if c.isBad() {
   365  		return nil, driver.ErrBadConn
   366  	}
   367  	if c.currTx != nil {
   368  		return nil, errors.New("fakedb: already in a transaction")
   369  	}
   370  	c.touchMem()
   371  	c.currTx = &fakeTx{c: c}
   372  	return c.currTx, nil
   373  }
   374  
   375  var hookPostCloseConn struct {
   376  	sync.Mutex
   377  	fn func(*fakeConn, error)
   378  }
   379  
   380  func setHookpostCloseConn(fn func(*fakeConn, error)) {
   381  	hookPostCloseConn.Lock()
   382  	defer hookPostCloseConn.Unlock()
   383  	hookPostCloseConn.fn = fn
   384  }
   385  
   386  var testStrictClose *testing.T
   387  
   388  // setStrictFakeConnClose sets the t to Errorf on when fakeConn.Close
   389  // fails to close. If nil, the check is disabled.
   390  func setStrictFakeConnClose(t *testing.T) {
   391  	testStrictClose = t
   392  }
   393  
   394  func (c *fakeConn) ResetSession(ctx context.Context) error {
   395  	c.dirtySession = false
   396  	if c.isBad() {
   397  		return driver.ErrBadConn
   398  	}
   399  	return nil
   400  }
   401  
   402  func (c *fakeConn) Close() (err error) {
   403  	drv := fdriver.(*fakeDriver)
   404  	defer func() {
   405  		if err != nil && testStrictClose != nil {
   406  			testStrictClose.Errorf("failed to close a test fakeConn: %v", err)
   407  		}
   408  		hookPostCloseConn.Lock()
   409  		fn := hookPostCloseConn.fn
   410  		hookPostCloseConn.Unlock()
   411  		if fn != nil {
   412  			fn(c, err)
   413  		}
   414  		if err == nil {
   415  			drv.mu.Lock()
   416  			drv.closeCount++
   417  			drv.mu.Unlock()
   418  		}
   419  	}()
   420  	c.touchMem()
   421  	if c.currTx != nil {
   422  		return errors.New("fakedb: can't close fakeConn; in a Transaction")
   423  	}
   424  	if c.db == nil {
   425  		return errors.New("fakedb: can't close fakeConn; already closed")
   426  	}
   427  	if c.stmtsMade > c.stmtsClosed {
   428  		return errors.New("fakedb: can't close; dangling statement(s)")
   429  	}
   430  	c.db = nil
   431  	return nil
   432  }
   433  
   434  func checkSubsetTypes(allowAny bool, args []driver.NamedValue) error {
   435  	for _, arg := range args {
   436  		switch arg.Value.(type) {
   437  		case int64, float64, bool, nil, []byte, string, time.Time:
   438  		default:
   439  			if !allowAny {
   440  				return fmt.Errorf("fakedb: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value)
   441  			}
   442  		}
   443  	}
   444  	return nil
   445  }
   446  
   447  func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
   448  	// Ensure that ExecContext is called if available.
   449  	panic("ExecContext was not called.")
   450  }
   451  
   452  func (c *fakeConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
   453  	// This is an optional interface, but it's implemented here
   454  	// just to check that all the args are of the proper types.
   455  	// ErrSkip is returned so the caller acts as if we didn't
   456  	// implement this at all.
   457  	err := checkSubsetTypes(c.db.allowAny, args)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  	return nil, driver.ErrSkip
   462  }
   463  
   464  func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
   465  	// Ensure that ExecContext is called if available.
   466  	panic("QueryContext was not called.")
   467  }
   468  
   469  func (c *fakeConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
   470  	// This is an optional interface, but it's implemented here
   471  	// just to check that all the args are of the proper types.
   472  	// ErrSkip is returned so the caller acts as if we didn't
   473  	// implement this at all.
   474  	err := checkSubsetTypes(c.db.allowAny, args)
   475  	if err != nil {
   476  		return nil, err
   477  	}
   478  	return nil, driver.ErrSkip
   479  }
   480  
   481  func errf(msg string, args ...interface{}) error {
   482  	return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
   483  }
   484  
   485  // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
   486  // (note that where columns must always contain ? marks,
   487  //  just a limitation for fakedb)
   488  func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
   489  	if len(parts) != 3 {
   490  		stmt.Close()
   491  		return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
   492  	}
   493  	stmt.table = parts[0]
   494  
   495  	stmt.colName = strings.Split(parts[1], ",")
   496  	for n, colspec := range strings.Split(parts[2], ",") {
   497  		if colspec == "" {
   498  			continue
   499  		}
   500  		nameVal := strings.Split(colspec, "=")
   501  		if len(nameVal) != 2 {
   502  			stmt.Close()
   503  			return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
   504  		}
   505  		column, value := nameVal[0], nameVal[1]
   506  		_, ok := c.db.columnType(stmt.table, column)
   507  		if !ok {
   508  			stmt.Close()
   509  			return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
   510  		}
   511  		if !strings.HasPrefix(value, "?") {
   512  			stmt.Close()
   513  			return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
   514  				stmt.table, column)
   515  		}
   516  		stmt.placeholders++
   517  		stmt.whereCol = append(stmt.whereCol, boundCol{Column: column, Placeholder: value, Ordinal: stmt.placeholders})
   518  	}
   519  	return stmt, nil
   520  }
   521  
   522  // parts are table|col=type,col2=type2
   523  func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
   524  	if len(parts) != 2 {
   525  		stmt.Close()
   526  		return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
   527  	}
   528  	stmt.table = parts[0]
   529  	for n, colspec := range strings.Split(parts[1], ",") {
   530  		nameType := strings.Split(colspec, "=")
   531  		if len(nameType) != 2 {
   532  			stmt.Close()
   533  			return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
   534  		}
   535  		stmt.colName = append(stmt.colName, nameType[0])
   536  		stmt.colType = append(stmt.colType, nameType[1])
   537  	}
   538  	return stmt, nil
   539  }
   540  
   541  // parts are table|col=?,col2=val
   542  func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
   543  	if len(parts) != 2 {
   544  		stmt.Close()
   545  		return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
   546  	}
   547  	stmt.table = parts[0]
   548  	for n, colspec := range strings.Split(parts[1], ",") {
   549  		nameVal := strings.Split(colspec, "=")
   550  		if len(nameVal) != 2 {
   551  			stmt.Close()
   552  			return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
   553  		}
   554  		column, value := nameVal[0], nameVal[1]
   555  		ctype, ok := c.db.columnType(stmt.table, column)
   556  		if !ok {
   557  			stmt.Close()
   558  			return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
   559  		}
   560  		stmt.colName = append(stmt.colName, column)
   561  
   562  		if !strings.HasPrefix(value, "?") {
   563  			var subsetVal interface{}
   564  			// Convert to driver subset type
   565  			switch ctype {
   566  			case "string":
   567  				subsetVal = []byte(value)
   568  			case "blob":
   569  				subsetVal = []byte(value)
   570  			case "int32":
   571  				i, err := strconv.Atoi(value)
   572  				if err != nil {
   573  					stmt.Close()
   574  					return nil, errf("invalid conversion to int32 from %q", value)
   575  				}
   576  				subsetVal = int64(i) // int64 is a subset type, but not int32
   577  			default:
   578  				stmt.Close()
   579  				return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
   580  			}
   581  			stmt.colValue = append(stmt.colValue, subsetVal)
   582  		} else {
   583  			stmt.placeholders++
   584  			stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
   585  			stmt.colValue = append(stmt.colValue, value)
   586  		}
   587  	}
   588  	return stmt, nil
   589  }
   590  
   591  // hook to simulate broken connections
   592  var hookPrepareBadConn func() bool
   593  
   594  func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
   595  	panic("use PrepareContext")
   596  }
   597  
   598  func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
   599  	c.numPrepare++
   600  	if c.db == nil {
   601  		panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
   602  	}
   603  
   604  	if c.stickyBad || (hookPrepareBadConn != nil && hookPrepareBadConn()) {
   605  		return nil, driver.ErrBadConn
   606  	}
   607  
   608  	c.touchMem()
   609  	var firstStmt, prev *fakeStmt
   610  	for _, query := range strings.Split(query, ";") {
   611  		parts := strings.Split(query, "|")
   612  		if len(parts) < 1 {
   613  			return nil, errf("empty query")
   614  		}
   615  		stmt := &fakeStmt{q: query, c: c, memToucher: c}
   616  		if firstStmt == nil {
   617  			firstStmt = stmt
   618  		}
   619  		if len(parts) >= 3 {
   620  			switch parts[0] {
   621  			case "PANIC":
   622  				stmt.panic = parts[1]
   623  				parts = parts[2:]
   624  			case "WAIT":
   625  				wait, err := time.ParseDuration(parts[1])
   626  				if err != nil {
   627  					return nil, errf("expected section after WAIT to be a duration, got %q %v", parts[1], err)
   628  				}
   629  				parts = parts[2:]
   630  				stmt.wait = wait
   631  			}
   632  		}
   633  		cmd := parts[0]
   634  		stmt.cmd = cmd
   635  		parts = parts[1:]
   636  
   637  		if c.waiter != nil {
   638  			c.waiter(ctx)
   639  		}
   640  
   641  		if stmt.wait > 0 {
   642  			wait := time.NewTimer(stmt.wait)
   643  			select {
   644  			case <-wait.C:
   645  			case <-ctx.Done():
   646  				wait.Stop()
   647  				return nil, ctx.Err()
   648  			}
   649  		}
   650  
   651  		c.incrStat(&c.stmtsMade)
   652  		var err error
   653  		switch cmd {
   654  		case "WIPE":
   655  			// Nothing
   656  		case "SELECT":
   657  			stmt, err = c.prepareSelect(stmt, parts)
   658  		case "CREATE":
   659  			stmt, err = c.prepareCreate(stmt, parts)
   660  		case "INSERT":
   661  			stmt, err = c.prepareInsert(stmt, parts)
   662  		case "NOSERT":
   663  			// Do all the prep-work like for an INSERT but don't actually insert the row.
   664  			// Used for some of the concurrent tests.
   665  			stmt, err = c.prepareInsert(stmt, parts)
   666  		default:
   667  			stmt.Close()
   668  			return nil, errf("unsupported command type %q", cmd)
   669  		}
   670  		if err != nil {
   671  			return nil, err
   672  		}
   673  		if prev != nil {
   674  			prev.next = stmt
   675  		}
   676  		prev = stmt
   677  	}
   678  	return firstStmt, nil
   679  }
   680  
   681  func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
   682  	if s.panic == "ColumnConverter" {
   683  		panic(s.panic)
   684  	}
   685  	if len(s.placeholderConverter) == 0 {
   686  		return driver.DefaultParameterConverter
   687  	}
   688  	return s.placeholderConverter[idx]
   689  }
   690  
   691  func (s *fakeStmt) Close() error {
   692  	if s.panic == "Close" {
   693  		panic(s.panic)
   694  	}
   695  	if s.c == nil {
   696  		panic("nil conn in fakeStmt.Close")
   697  	}
   698  	if s.c.db == nil {
   699  		panic("in fakeStmt.Close, conn's db is nil (already closed)")
   700  	}
   701  	s.touchMem()
   702  	if !s.closed {
   703  		s.c.incrStat(&s.c.stmtsClosed)
   704  		s.closed = true
   705  	}
   706  	if s.next != nil {
   707  		s.next.Close()
   708  	}
   709  	return nil
   710  }
   711  
   712  var errClosed = errors.New("fakedb: statement has been closed")
   713  
   714  // hook to simulate broken connections
   715  var hookExecBadConn func() bool
   716  
   717  func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
   718  	panic("Using ExecContext")
   719  }
   720  func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
   721  	if s.panic == "Exec" {
   722  		panic(s.panic)
   723  	}
   724  	if s.closed {
   725  		return nil, errClosed
   726  	}
   727  
   728  	if s.c.stickyBad || (hookExecBadConn != nil && hookExecBadConn()) {
   729  		return nil, driver.ErrBadConn
   730  	}
   731  	if s.c.isDirtyAndMark() {
   732  		return nil, errors.New("fakedb: session is dirty")
   733  	}
   734  
   735  	err := checkSubsetTypes(s.c.db.allowAny, args)
   736  	if err != nil {
   737  		return nil, err
   738  	}
   739  	s.touchMem()
   740  
   741  	if s.wait > 0 {
   742  		time.Sleep(s.wait)
   743  	}
   744  
   745  	select {
   746  	default:
   747  	case <-ctx.Done():
   748  		return nil, ctx.Err()
   749  	}
   750  
   751  	db := s.c.db
   752  	switch s.cmd {
   753  	case "WIPE":
   754  		db.wipe()
   755  		return driver.ResultNoRows, nil
   756  	case "CREATE":
   757  		if err := db.createTable(s.table, s.colName, s.colType); err != nil {
   758  			return nil, err
   759  		}
   760  		return driver.ResultNoRows, nil
   761  	case "INSERT":
   762  		return s.execInsert(args, true)
   763  	case "NOSERT":
   764  		// Do all the prep-work like for an INSERT but don't actually insert the row.
   765  		// Used for some of the concurrent tests.
   766  		return s.execInsert(args, false)
   767  	}
   768  	return nil, fmt.Errorf("fakedb: unimplemented statement Exec command type of %q", s.cmd)
   769  }
   770  
   771  // When doInsert is true, add the row to the table.
   772  // When doInsert is false do prep-work and error checking, but don't
   773  // actually add the row to the table.
   774  func (s *fakeStmt) execInsert(args []driver.NamedValue, doInsert bool) (driver.Result, error) {
   775  	db := s.c.db
   776  	if len(args) != s.placeholders {
   777  		panic("error in pkg db; should only get here if size is correct")
   778  	}
   779  	db.mu.Lock()
   780  	t, ok := db.table(s.table)
   781  	db.mu.Unlock()
   782  	if !ok {
   783  		return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
   784  	}
   785  
   786  	t.mu.Lock()
   787  	defer t.mu.Unlock()
   788  
   789  	var cols []interface{}
   790  	if doInsert {
   791  		cols = make([]interface{}, len(t.colname))
   792  	}
   793  	argPos := 0
   794  	for n, colname := range s.colName {
   795  		colidx := t.columnIndex(colname)
   796  		if colidx == -1 {
   797  			return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
   798  		}
   799  		var val interface{}
   800  		if strvalue, ok := s.colValue[n].(string); ok && strings.HasPrefix(strvalue, "?") {
   801  			if strvalue == "?" {
   802  				val = args[argPos].Value
   803  			} else {
   804  				// Assign value from argument placeholder name.
   805  				for _, a := range args {
   806  					if a.Name == strvalue[1:] {
   807  						val = a.Value
   808  						break
   809  					}
   810  				}
   811  			}
   812  			argPos++
   813  		} else {
   814  			val = s.colValue[n]
   815  		}
   816  		if doInsert {
   817  			cols[colidx] = val
   818  		}
   819  	}
   820  
   821  	if doInsert {
   822  		t.rows = append(t.rows, &row{cols: cols})
   823  	}
   824  	return driver.RowsAffected(1), nil
   825  }
   826  
   827  // hook to simulate broken connections
   828  var hookQueryBadConn func() bool
   829  
   830  func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
   831  	panic("Use QueryContext")
   832  }
   833  
   834  func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
   835  	if s.panic == "Query" {
   836  		panic(s.panic)
   837  	}
   838  	if s.closed {
   839  		return nil, errClosed
   840  	}
   841  
   842  	if s.c.stickyBad || (hookQueryBadConn != nil && hookQueryBadConn()) {
   843  		return nil, driver.ErrBadConn
   844  	}
   845  	if s.c.isDirtyAndMark() {
   846  		return nil, errors.New("fakedb: session is dirty")
   847  	}
   848  
   849  	err := checkSubsetTypes(s.c.db.allowAny, args)
   850  	if err != nil {
   851  		return nil, err
   852  	}
   853  
   854  	s.touchMem()
   855  	db := s.c.db
   856  	if len(args) != s.placeholders {
   857  		panic("error in pkg db; should only get here if size is correct")
   858  	}
   859  
   860  	setMRows := make([][]*row, 0, 1)
   861  	setColumns := make([][]string, 0, 1)
   862  	setColType := make([][]string, 0, 1)
   863  
   864  	for {
   865  		db.mu.Lock()
   866  		t, ok := db.table(s.table)
   867  		db.mu.Unlock()
   868  		if !ok {
   869  			return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
   870  		}
   871  
   872  		if s.table == "magicquery" {
   873  			if len(s.whereCol) == 2 && s.whereCol[0].Column == "op" && s.whereCol[1].Column == "millis" {
   874  				if args[0].Value == "sleep" {
   875  					time.Sleep(time.Duration(args[1].Value.(int64)) * time.Millisecond)
   876  				}
   877  			}
   878  		}
   879  
   880  		t.mu.Lock()
   881  
   882  		colIdx := make(map[string]int) // select column name -> column index in table
   883  		for _, name := range s.colName {
   884  			idx := t.columnIndex(name)
   885  			if idx == -1 {
   886  				t.mu.Unlock()
   887  				return nil, fmt.Errorf("fakedb: unknown column name %q", name)
   888  			}
   889  			colIdx[name] = idx
   890  		}
   891  
   892  		mrows := []*row{}
   893  	rows:
   894  		for _, trow := range t.rows {
   895  			// Process the where clause, skipping non-match rows. This is lazy
   896  			// and just uses fmt.Sprintf("%v") to test equality. Good enough
   897  			// for test code.
   898  			for _, wcol := range s.whereCol {
   899  				idx := t.columnIndex(wcol.Column)
   900  				if idx == -1 {
   901  					t.mu.Unlock()
   902  					return nil, fmt.Errorf("fakedb: invalid where clause column %q", wcol)
   903  				}
   904  				tcol := trow.cols[idx]
   905  				if bs, ok := tcol.([]byte); ok {
   906  					// lazy hack to avoid sprintf %v on a []byte
   907  					tcol = string(bs)
   908  				}
   909  				var argValue interface{}
   910  				if wcol.Placeholder == "?" {
   911  					argValue = args[wcol.Ordinal-1].Value
   912  				} else {
   913  					// Assign arg value from placeholder name.
   914  					for _, a := range args {
   915  						if a.Name == wcol.Placeholder[1:] {
   916  							argValue = a.Value
   917  							break
   918  						}
   919  					}
   920  				}
   921  				if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", argValue) {
   922  					continue rows
   923  				}
   924  			}
   925  			mrow := &row{cols: make([]interface{}, len(s.colName))}
   926  			for seli, name := range s.colName {
   927  				mrow.cols[seli] = trow.cols[colIdx[name]]
   928  			}
   929  			mrows = append(mrows, mrow)
   930  		}
   931  
   932  		var colType []string
   933  		for _, column := range s.colName {
   934  			colType = append(colType, t.coltype[t.columnIndex(column)])
   935  		}
   936  
   937  		t.mu.Unlock()
   938  
   939  		setMRows = append(setMRows, mrows)
   940  		setColumns = append(setColumns, s.colName)
   941  		setColType = append(setColType, colType)
   942  
   943  		if s.next == nil {
   944  			break
   945  		}
   946  		s = s.next
   947  	}
   948  
   949  	cursor := &rowsCursor{
   950  		parentMem: s.c,
   951  		posRow:    -1,
   952  		rows:      setMRows,
   953  		cols:      setColumns,
   954  		colType:   setColType,
   955  		errPos:    -1,
   956  	}
   957  	return cursor, nil
   958  }
   959  
   960  func (s *fakeStmt) NumInput() int {
   961  	if s.panic == "NumInput" {
   962  		panic(s.panic)
   963  	}
   964  	return s.placeholders
   965  }
   966  
   967  // hook to simulate broken connections
   968  var hookCommitBadConn func() bool
   969  
   970  func (tx *fakeTx) Commit() error {
   971  	tx.c.currTx = nil
   972  	if hookCommitBadConn != nil && hookCommitBadConn() {
   973  		return driver.ErrBadConn
   974  	}
   975  	tx.c.touchMem()
   976  	return nil
   977  }
   978  
   979  // hook to simulate broken connections
   980  var hookRollbackBadConn func() bool
   981  
   982  func (tx *fakeTx) Rollback() error {
   983  	tx.c.currTx = nil
   984  	if hookRollbackBadConn != nil && hookRollbackBadConn() {
   985  		return driver.ErrBadConn
   986  	}
   987  	tx.c.touchMem()
   988  	return nil
   989  }
   990  
   991  type rowsCursor struct {
   992  	parentMem memToucher
   993  	cols      [][]string
   994  	colType   [][]string
   995  	posSet    int
   996  	posRow    int
   997  	rows      [][]*row
   998  	closed    bool
   999  
  1000  	// errPos and err are for making Next return early with error.
  1001  	errPos int
  1002  	err    error
  1003  
  1004  	// a clone of slices to give out to clients, indexed by the
  1005  	// original slice's first byte address.  we clone them
  1006  	// just so we're able to corrupt them on close.
  1007  	bytesClone map[*byte][]byte
  1008  
  1009  	// Every operation writes to line to enable the race detector
  1010  	// check for data races.
  1011  	// This is separate from the fakeConn.line to allow for drivers that
  1012  	// can start multiple queries on the same transaction at the same time.
  1013  	line int64
  1014  }
  1015  
  1016  func (rc *rowsCursor) touchMem() {
  1017  	rc.parentMem.touchMem()
  1018  	rc.line++
  1019  }
  1020  
  1021  func (rc *rowsCursor) Close() error {
  1022  	rc.touchMem()
  1023  	rc.parentMem.touchMem()
  1024  	rc.closed = true
  1025  	return nil
  1026  }
  1027  
  1028  func (rc *rowsCursor) Columns() []string {
  1029  	return rc.cols[rc.posSet]
  1030  }
  1031  
  1032  func (rc *rowsCursor) ColumnTypeScanType(index int) reflect.Type {
  1033  	return colTypeToReflectType(rc.colType[rc.posSet][index])
  1034  }
  1035  
  1036  var rowsCursorNextHook func(dest []driver.Value) error
  1037  
  1038  func (rc *rowsCursor) Next(dest []driver.Value) error {
  1039  	if rowsCursorNextHook != nil {
  1040  		return rowsCursorNextHook(dest)
  1041  	}
  1042  
  1043  	if rc.closed {
  1044  		return errors.New("fakedb: cursor is closed")
  1045  	}
  1046  	rc.touchMem()
  1047  	rc.posRow++
  1048  	if rc.posRow == rc.errPos {
  1049  		return rc.err
  1050  	}
  1051  	if rc.posRow >= len(rc.rows[rc.posSet]) {
  1052  		return io.EOF // per interface spec
  1053  	}
  1054  	for i, v := range rc.rows[rc.posSet][rc.posRow].cols {
  1055  		// TODO(bradfitz): convert to subset types? naah, I
  1056  		// think the subset types should only be input to
  1057  		// driver, but the sql package should be able to handle
  1058  		// a wider range of types coming out of drivers. all
  1059  		// for ease of drivers, and to prevent drivers from
  1060  		// messing up conversions or doing them differently.
  1061  		dest[i] = v
  1062  
  1063  		if bs, ok := v.([]byte); ok {
  1064  			if rc.bytesClone == nil {
  1065  				rc.bytesClone = make(map[*byte][]byte)
  1066  			}
  1067  			clone, ok := rc.bytesClone[&bs[0]]
  1068  			if !ok {
  1069  				clone = make([]byte, len(bs))
  1070  				copy(clone, bs)
  1071  				rc.bytesClone[&bs[0]] = clone
  1072  			}
  1073  			dest[i] = clone
  1074  		}
  1075  	}
  1076  	return nil
  1077  }
  1078  
  1079  func (rc *rowsCursor) HasNextResultSet() bool {
  1080  	rc.touchMem()
  1081  	return rc.posSet < len(rc.rows)-1
  1082  }
  1083  
  1084  func (rc *rowsCursor) NextResultSet() error {
  1085  	rc.touchMem()
  1086  	if rc.HasNextResultSet() {
  1087  		rc.posSet++
  1088  		rc.posRow = -1
  1089  		return nil
  1090  	}
  1091  	return io.EOF // Per interface spec.
  1092  }
  1093  
  1094  // fakeDriverString is like driver.String, but indirects pointers like
  1095  // DefaultValueConverter.
  1096  //
  1097  // This could be surprising behavior to retroactively apply to
  1098  // driver.String now that Go1 is out, but this is convenient for
  1099  // our TestPointerParamsAndScans.
  1100  //
  1101  type fakeDriverString struct{}
  1102  
  1103  func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
  1104  	switch c := v.(type) {
  1105  	case string, []byte:
  1106  		return v, nil
  1107  	case *string:
  1108  		if c == nil {
  1109  			return nil, nil
  1110  		}
  1111  		return *c, nil
  1112  	}
  1113  	return fmt.Sprintf("%v", v), nil
  1114  }
  1115  
  1116  type anyTypeConverter struct{}
  1117  
  1118  func (anyTypeConverter) ConvertValue(v interface{}) (driver.Value, error) {
  1119  	return v, nil
  1120  }
  1121  
  1122  func converterForType(typ string) driver.ValueConverter {
  1123  	switch typ {
  1124  	case "bool":
  1125  		return driver.Bool
  1126  	case "nullbool":
  1127  		return driver.Null{Converter: driver.Bool}
  1128  	case "int32":
  1129  		return driver.Int32
  1130  	case "string":
  1131  		return driver.NotNull{Converter: fakeDriverString{}}
  1132  	case "nullstring":
  1133  		return driver.Null{Converter: fakeDriverString{}}
  1134  	case "int64":
  1135  		// TODO(coopernurse): add type-specific converter
  1136  		return driver.NotNull{Converter: driver.DefaultParameterConverter}
  1137  	case "nullint64":
  1138  		// TODO(coopernurse): add type-specific converter
  1139  		return driver.Null{Converter: driver.DefaultParameterConverter}
  1140  	case "float64":
  1141  		// TODO(coopernurse): add type-specific converter
  1142  		return driver.NotNull{Converter: driver.DefaultParameterConverter}
  1143  	case "nullfloat64":
  1144  		// TODO(coopernurse): add type-specific converter
  1145  		return driver.Null{Converter: driver.DefaultParameterConverter}
  1146  	case "datetime":
  1147  		return driver.DefaultParameterConverter
  1148  	case "any":
  1149  		return anyTypeConverter{}
  1150  	}
  1151  	panic("invalid fakedb column type of " + typ)
  1152  }
  1153  
  1154  func colTypeToReflectType(typ string) reflect.Type {
  1155  	switch typ {
  1156  	case "bool":
  1157  		return reflect.TypeOf(false)
  1158  	case "nullbool":
  1159  		return reflect.TypeOf(NullBool{})
  1160  	case "int32":
  1161  		return reflect.TypeOf(int32(0))
  1162  	case "string":
  1163  		return reflect.TypeOf("")
  1164  	case "nullstring":
  1165  		return reflect.TypeOf(NullString{})
  1166  	case "int64":
  1167  		return reflect.TypeOf(int64(0))
  1168  	case "nullint64":
  1169  		return reflect.TypeOf(NullInt64{})
  1170  	case "float64":
  1171  		return reflect.TypeOf(float64(0))
  1172  	case "nullfloat64":
  1173  		return reflect.TypeOf(NullFloat64{})
  1174  	case "datetime":
  1175  		return reflect.TypeOf(time.Time{})
  1176  	case "any":
  1177  		return reflect.TypeOf(new(interface{})).Elem()
  1178  	}
  1179  	panic("invalid fakedb column type of " + typ)
  1180  }
  1181  

View as plain text