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.
22 // fakeDriver is a fake database that implements Go's driver.Driver
23 // interface, just for testing.
25 // It speaks a query language that's semantically similar to but
26 // syntantically different and simpler than SQL. The syntax is as
30 // CREATE|<tablename>|<col>=<type>,<col>=<type>,...
31 // where types are: "string", [u]int{8,16,32,64}, "bool"
32 // INSERT|<tablename>|col=val,col2=val2,col3=?
33 // SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
35 // When opening a fakeDriver's database, it starts empty with no
36 // tables. All tables and data are stored in memory only.
37 type fakeDriver struct {
38 mu sync.Mutex // guards 3 following fields
39 openCount int // conn opens
40 closeCount int // conn closes
41 dbs map[string]*fakeDB
49 tables map[string]*table
60 func (t *table) columnIndex(name string) int {
61 for n, nname := range t.colname {
70 cols []interface{} // must be same size as its table colname + coltype
73 func (r *row) clone() *row {
74 nrow := &row{cols: make([]interface{}, len(r.cols))}
75 copy(nrow.cols, r.cols)
79 type fakeConn struct {
80 db *fakeDB // where to return ourselves to
92 func (c *fakeConn) incrStat(v *int) {
102 type fakeStmt struct {
104 q string // just for debugging
111 colName []string // used by CREATE, INSERT, SELECT (selected columns)
112 colType []string // used by CREATE
113 colValue []interface{} // used by INSERT (mix of strings and "?" for bound params)
114 placeholders int // used by INSERT/SELECT: number of ? params
116 whereCol []string // used by SELECT (all placeholders)
118 placeholderConverter []driver.ValueConverter // used by INSERT
121 var fdriver driver.Driver = &fakeDriver{}
124 Register("test", fdriver)
127 // Supports dsn forms:
129 // <dbname>;<opts> (only currently supported option is `badConn`,
130 // which causes driver.ErrBadConn to be returned on
131 // every other conn.Begin())
132 func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
133 parts := strings.Split(dsn, ";")
135 return nil, errors.New("fakedb: no database name")
144 conn := &fakeConn{db: db}
146 if len(parts) >= 2 && parts[1] == "badConn" {
152 func (d *fakeDriver) getDB(name string) *fakeDB {
156 d.dbs = make(map[string]*fakeDB)
158 db, ok := d.dbs[name]
160 db = &fakeDB{name: name}
166 func (db *fakeDB) wipe() {
172 func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
175 if db.tables == nil {
176 db.tables = make(map[string]*table)
178 if _, exist := db.tables[name]; exist {
179 return fmt.Errorf("table %q already exists", name)
181 if len(columnNames) != len(columnTypes) {
182 return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d",
183 name, len(columnNames), len(columnTypes))
185 db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
189 // must be called with db.mu lock held
190 func (db *fakeDB) table(table string) (*table, bool) {
191 if db.tables == nil {
194 t, ok := db.tables[table]
198 func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
201 t, ok := db.table(table)
205 for n, cname := range t.colname {
207 return t.coltype[n], true
213 func (c *fakeConn) isBad() bool {
214 // if not simulating bad conn, do nothing
218 // alternate between bad conn and not bad conn
219 c.db.badConn = !c.db.badConn
223 func (c *fakeConn) Begin() (driver.Tx, error) {
225 return nil, driver.ErrBadConn
228 return nil, errors.New("already in a transaction")
230 c.currTx = &fakeTx{c: c}
234 var hookPostCloseConn struct {
236 fn func(*fakeConn, error)
239 func setHookpostCloseConn(fn func(*fakeConn, error)) {
240 hookPostCloseConn.Lock()
241 defer hookPostCloseConn.Unlock()
242 hookPostCloseConn.fn = fn
245 var testStrictClose *testing.T
247 // setStrictFakeConnClose sets the t to Errorf on when fakeConn.Close
248 // fails to close. If nil, the check is disabled.
249 func setStrictFakeConnClose(t *testing.T) {
253 func (c *fakeConn) Close() (err error) {
254 drv := fdriver.(*fakeDriver)
256 if err != nil && testStrictClose != nil {
257 testStrictClose.Errorf("failed to close a test fakeConn: %v", err)
259 hookPostCloseConn.Lock()
260 fn := hookPostCloseConn.fn
261 hookPostCloseConn.Unlock()
272 return errors.New("can't close fakeConn; in a Transaction")
275 return errors.New("can't close fakeConn; already closed")
277 if c.stmtsMade > c.stmtsClosed {
278 return errors.New("can't close; dangling statement(s)")
284 func checkSubsetTypes(args []driver.Value) error {
285 for n, arg := range args {
287 case int64, float64, bool, nil, []byte, string, time.Time:
289 return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
295 func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
296 // This is an optional interface, but it's implemented here
297 // just to check that all the args are of the proper types.
298 // ErrSkip is returned so the caller acts as if we didn't
299 // implement this at all.
300 err := checkSubsetTypes(args)
304 return nil, driver.ErrSkip
307 func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
308 // This is an optional interface, but it's implemented here
309 // just to check that all the args are of the proper types.
310 // ErrSkip is returned so the caller acts as if we didn't
311 // implement this at all.
312 err := checkSubsetTypes(args)
316 return nil, driver.ErrSkip
319 func errf(msg string, args ...interface{}) error {
320 return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
323 // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
324 // (note that where columns must always contain ? marks,
325 // just a limitation for fakedb)
326 func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
329 return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
331 stmt.table = parts[0]
332 stmt.colName = strings.Split(parts[1], ",")
333 for n, colspec := range strings.Split(parts[2], ",") {
337 nameVal := strings.Split(colspec, "=")
338 if len(nameVal) != 2 {
340 return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
342 column, value := nameVal[0], nameVal[1]
343 _, ok := c.db.columnType(stmt.table, column)
346 return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
350 return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
353 stmt.whereCol = append(stmt.whereCol, column)
359 // parts are table|col=type,col2=type2
360 func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
363 return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
365 stmt.table = parts[0]
366 for n, colspec := range strings.Split(parts[1], ",") {
367 nameType := strings.Split(colspec, "=")
368 if len(nameType) != 2 {
370 return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
372 stmt.colName = append(stmt.colName, nameType[0])
373 stmt.colType = append(stmt.colType, nameType[1])
378 // parts are table|col=?,col2=val
379 func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
382 return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
384 stmt.table = parts[0]
385 for n, colspec := range strings.Split(parts[1], ",") {
386 nameVal := strings.Split(colspec, "=")
387 if len(nameVal) != 2 {
389 return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
391 column, value := nameVal[0], nameVal[1]
392 ctype, ok := c.db.columnType(stmt.table, column)
395 return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
397 stmt.colName = append(stmt.colName, column)
400 var subsetVal interface{}
401 // Convert to driver subset type
404 subsetVal = []byte(value)
406 subsetVal = []byte(value)
408 i, err := strconv.Atoi(value)
411 return nil, errf("invalid conversion to int32 from %q", value)
413 subsetVal = int64(i) // int64 is a subset type, but not int32
416 return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
418 stmt.colValue = append(stmt.colValue, subsetVal)
421 stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
422 stmt.colValue = append(stmt.colValue, "?")
428 func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
431 panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
433 parts := strings.Split(query, "|")
435 return nil, errf("empty query")
439 stmt := &fakeStmt{q: query, c: c, cmd: cmd}
440 c.incrStat(&c.stmtsMade)
445 return c.prepareSelect(stmt, parts)
447 return c.prepareCreate(stmt, parts)
449 return c.prepareInsert(stmt, parts)
452 return nil, errf("unsupported command type %q", cmd)
457 func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
458 if len(s.placeholderConverter) == 0 {
459 return driver.DefaultParameterConverter
461 return s.placeholderConverter[idx]
464 func (s *fakeStmt) Close() error {
466 panic("nil conn in fakeStmt.Close")
469 panic("in fakeStmt.Close, conn's db is nil (already closed)")
472 s.c.incrStat(&s.c.stmtsClosed)
478 var errClosed = errors.New("fakedb: statement has been closed")
480 func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
482 return nil, errClosed
484 err := checkSubsetTypes(args)
493 return driver.ResultNoRows, nil
495 if err := db.createTable(s.table, s.colName, s.colType); err != nil {
498 return driver.ResultNoRows, nil
500 return s.execInsert(args)
502 fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
503 return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
506 func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) {
508 if len(args) != s.placeholders {
509 panic("error in pkg db; should only get here if size is correct")
512 t, ok := db.table(s.table)
515 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
521 cols := make([]interface{}, len(t.colname))
523 for n, colname := range s.colName {
524 colidx := t.columnIndex(colname)
526 return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
529 if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
538 t.rows = append(t.rows, &row{cols: cols})
539 return driver.RowsAffected(1), nil
542 func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
544 return nil, errClosed
546 err := checkSubsetTypes(args)
552 if len(args) != s.placeholders {
553 panic("error in pkg db; should only get here if size is correct")
557 t, ok := db.table(s.table)
560 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
563 if s.table == "magicquery" {
564 if len(s.whereCol) == 2 && s.whereCol[0] == "op" && s.whereCol[1] == "millis" {
565 if args[0] == "sleep" {
566 time.Sleep(time.Duration(args[1].(int64)) * time.Millisecond)
574 colIdx := make(map[string]int) // select column name -> column index in table
575 for _, name := range s.colName {
576 idx := t.columnIndex(name)
578 return nil, fmt.Errorf("fakedb: unknown column name %q", name)
585 for _, trow := range t.rows {
586 // Process the where clause, skipping non-match rows. This is lazy
587 // and just uses fmt.Sprintf("%v") to test equality. Good enough
589 for widx, wcol := range s.whereCol {
590 idx := t.columnIndex(wcol)
592 return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
594 tcol := trow.cols[idx]
595 if bs, ok := tcol.([]byte); ok {
596 // lazy hack to avoid sprintf %v on a []byte
599 if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
603 mrow := &row{cols: make([]interface{}, len(s.colName))}
604 for seli, name := range s.colName {
605 mrow.cols[seli] = trow.cols[colIdx[name]]
607 mrows = append(mrows, mrow)
610 cursor := &rowsCursor{
618 func (s *fakeStmt) NumInput() int {
619 return s.placeholders
622 func (tx *fakeTx) Commit() error {
627 func (tx *fakeTx) Rollback() error {
632 type rowsCursor struct {
638 // a clone of slices to give out to clients, indexed by the
639 // the original slice's first byte address. we clone them
640 // just so we're able to corrupt them on close.
641 bytesClone map[*byte][]byte
644 func (rc *rowsCursor) Close() error {
646 for _, bs := range rc.bytesClone {
647 bs[0] = 255 // first byte corrupted
654 func (rc *rowsCursor) Columns() []string {
658 func (rc *rowsCursor) Next(dest []driver.Value) error {
660 return errors.New("fakedb: cursor is closed")
663 if rc.pos >= len(rc.rows) {
664 return io.EOF // per interface spec
666 for i, v := range rc.rows[rc.pos].cols {
667 // TODO(bradfitz): convert to subset types? naah, I
668 // think the subset types should only be input to
669 // driver, but the sql package should be able to handle
670 // a wider range of types coming out of drivers. all
671 // for ease of drivers, and to prevent drivers from
672 // messing up conversions or doing them differently.
675 if bs, ok := v.([]byte); ok {
676 if rc.bytesClone == nil {
677 rc.bytesClone = make(map[*byte][]byte)
679 clone, ok := rc.bytesClone[&bs[0]]
681 clone = make([]byte, len(bs))
683 rc.bytesClone[&bs[0]] = clone
691 // fakeDriverString is like driver.String, but indirects pointers like
692 // DefaultValueConverter.
694 // This could be surprising behavior to retroactively apply to
695 // driver.String now that Go1 is out, but this is convenient for
696 // our TestPointerParamsAndScans.
698 type fakeDriverString struct{}
700 func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
701 switch c := v.(type) {
710 return fmt.Sprintf("%v", v), nil
713 func converterForType(typ string) driver.ValueConverter {
718 return driver.Null{Converter: driver.Bool}
722 return driver.NotNull{Converter: fakeDriverString{}}
724 return driver.Null{Converter: fakeDriverString{}}
726 // TODO(coopernurse): add type-specific converter
727 return driver.NotNull{Converter: driver.DefaultParameterConverter}
729 // TODO(coopernurse): add type-specific converter
730 return driver.Null{Converter: driver.DefaultParameterConverter}
732 // TODO(coopernurse): add type-specific converter
733 return driver.NotNull{Converter: driver.DefaultParameterConverter}
735 // TODO(coopernurse): add type-specific converter
736 return driver.Null{Converter: driver.DefaultParameterConverter}
738 return driver.DefaultParameterConverter
740 panic("invalid fakedb column type of " + typ)