diff options
Diffstat (limited to 'src/pkg/database/sql/fakedb_test.go')
-rw-r--r-- | src/pkg/database/sql/fakedb_test.go | 79 |
1 files changed, 69 insertions, 10 deletions
diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go index 184e7756c..55597f7de 100644 --- a/src/pkg/database/sql/fakedb_test.go +++ b/src/pkg/database/sql/fakedb_test.go @@ -31,7 +31,7 @@ var _ = log.Printf // INSERT|<tablename>|col=val,col2=val2,col3=? // SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=? // -// When opening a a fakeDriver's database, it starts empty with no +// When opening a fakeDriver's database, it starts empty with no // tables. All tables and data are stored in memory only. type fakeDriver struct { mu sync.Mutex @@ -42,9 +42,10 @@ type fakeDriver struct { type fakeDB struct { name string - mu sync.Mutex - free []*fakeConn - tables map[string]*table + mu sync.Mutex + free []*fakeConn + tables map[string]*table + badConn bool } type table struct { @@ -83,6 +84,7 @@ type fakeConn struct { stmtsMade int stmtsClosed int numPrepare int + bad bool } func (c *fakeConn) incrStat(v *int) { @@ -122,7 +124,9 @@ func init() { // Supports dsn forms: // <dbname> -// <dbname>;<opts> (no currently supported options) +// <dbname>;<opts> (only currently supported option is `badConn`, +// which causes driver.ErrBadConn to be returned on +// every other conn.Begin()) func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { parts := strings.Split(dsn, ";") if len(parts) < 1 { @@ -135,7 +139,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { d.mu.Lock() d.openCount++ d.mu.Unlock() - return &fakeConn{db: db}, nil + conn := &fakeConn{db: db} + + if len(parts) >= 2 && parts[1] == "badConn" { + conn.bad = true + } + return conn, nil } func (d *fakeDriver) getDB(name string) *fakeDB { @@ -199,7 +208,20 @@ func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { return "", false } +func (c *fakeConn) isBad() bool { + // if not simulating bad conn, do nothing + if !c.bad { + return false + } + // alternate between bad conn and not bad conn + c.db.badConn = !c.db.badConn + return c.db.badConn +} + func (c *fakeConn) Begin() (driver.Tx, error) { + if c.isBad() { + return nil, driver.ErrBadConn + } if c.currTx != nil { return nil, errors.New("already in a transaction") } @@ -234,7 +256,19 @@ func checkSubsetTypes(args []driver.Value) error { func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) { // This is an optional interface, but it's implemented here - // just to check that all the args of of the proper types. + // just to check that all the args are of the proper types. + // ErrSkip is returned so the caller acts as if we didn't + // implement this at all. + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + +func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) { + // This is an optional interface, but it's implemented here + // just to check that all the args are of the proper types. // ErrSkip is returned so the caller acts as if we didn't // implement this at all. err := checkSubsetTypes(args) @@ -249,7 +283,7 @@ func errf(msg string, args ...interface{}) error { } // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? -// (note that where where columns must always contain ? marks, +// (note that where columns must always contain ? marks, // just a limitation for fakedb) func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) { if len(parts) != 3 { @@ -383,6 +417,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { } func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { + if len(s.placeholderConverter) == 0 { + return driver.DefaultParameterConverter + } return s.placeholderConverter[idx] } @@ -598,6 +635,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { return nil } +// fakeDriverString is like driver.String, but indirects pointers like +// DefaultValueConverter. +// +// This could be surprising behavior to retroactively apply to +// driver.String now that Go1 is out, but this is convenient for +// our TestPointerParamsAndScans. +// +type fakeDriverString struct{} + +func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) { + switch c := v.(type) { + case string, []byte: + return v, nil + case *string: + if c == nil { + return nil, nil + } + return *c, nil + } + return fmt.Sprintf("%v", v), nil +} + func converterForType(typ string) driver.ValueConverter { switch typ { case "bool": @@ -607,9 +666,9 @@ func converterForType(typ string) driver.ValueConverter { case "int32": return driver.Int32 case "string": - return driver.NotNull{Converter: driver.String} + return driver.NotNull{Converter: fakeDriverString{}} case "nullstring": - return driver.Null{Converter: driver.String} + return driver.Null{Converter: fakeDriverString{}} case "int64": // TODO(coopernurse): add type-specific converter return driver.NotNull{Converter: driver.DefaultParameterConverter} |