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.
21 // fakeDriver is a fake database that implements Go's driver.Driver
22 // interface, just for testing.
24 // It speaks a query language that's semantically similar to but
25 // syntantically different and simpler than SQL. The syntax is as
29 // CREATE|<tablename>|<col>=<type>,<col>=<type>,...
30 // where types are: "string", [u]int{8,16,32,64}, "bool"
31 // INSERT|<tablename>|col=val,col2=val2,col3=?
32 // SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
34 // When opening a a fakeDriver's database, it starts empty with no
35 // tables. All tables and data are stored in memory only.
36 type fakeDriver struct {
39 dbs map[string]*fakeDB
47 tables map[string]*table
57 func (t *table) columnIndex(name string) int {
58 for n, nname := range t.colname {
67 cols []interface{} // must be same size as its table colname + coltype
70 func (r *row) clone() *row {
71 nrow := &row{cols: make([]interface{}, len(r.cols))}
72 copy(nrow.cols, r.cols)
76 type fakeConn struct {
77 db *fakeDB // where to return ourselves to
86 type fakeStmt struct {
88 q string // just for debugging
93 colName []string // used by CREATE, INSERT, SELECT (selected columns)
94 colType []string // used by CREATE
95 colValue []interface{} // used by INSERT (mix of strings and "?" for bound params)
96 placeholders int // used by INSERT/SELECT: number of ? params
98 whereCol []string // used by SELECT (all placeholders)
100 placeholderConverter []driver.ValueConverter // used by INSERT
103 var fdriver driver.Driver = &fakeDriver{}
106 Register("test", fdriver)
109 // Supports dsn forms:
112 func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
117 d.dbs = make(map[string]*fakeDB)
119 parts := strings.Split(dsn, ";")
121 return nil, errors.New("fakedb: no database name")
124 db, ok := d.dbs[name]
126 db = &fakeDB{name: name}
129 return &fakeConn{db: db}, nil
132 func (db *fakeDB) wipe() {
138 func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error {
141 if db.tables == nil {
142 db.tables = make(map[string]*table)
144 if _, exist := db.tables[name]; exist {
145 return fmt.Errorf("table %q already exists", name)
147 if len(columnNames) != len(columnTypes) {
148 return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d",
149 name, len(columnNames), len(columnTypes))
151 db.tables[name] = &table{colname: columnNames, coltype: columnTypes}
155 // must be called with db.mu lock held
156 func (db *fakeDB) table(table string) (*table, bool) {
157 if db.tables == nil {
160 t, ok := db.tables[table]
164 func (db *fakeDB) columnType(table, column string) (typ string, ok bool) {
167 t, ok := db.table(table)
171 for n, cname := range t.colname {
173 return t.coltype[n], true
179 func (c *fakeConn) Begin() (driver.Tx, error) {
181 return nil, errors.New("already in a transaction")
183 c.currTx = &fakeTx{c: c}
187 func (c *fakeConn) Close() error {
189 return errors.New("can't close; in a Transaction")
192 return errors.New("can't close; already closed")
198 func errf(msg string, args ...interface{}) error {
199 return errors.New("fakedb: " + fmt.Sprintf(msg, args...))
202 // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
203 // (note that where where columns must always contain ? marks,
204 // just a limitation for fakedb)
205 func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
207 return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
209 stmt.table = parts[0]
210 stmt.colName = strings.Split(parts[1], ",")
211 for n, colspec := range strings.Split(parts[2], ",") {
212 nameVal := strings.Split(colspec, "=")
213 if len(nameVal) != 2 {
214 return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
216 column, value := nameVal[0], nameVal[1]
217 _, ok := c.db.columnType(stmt.table, column)
219 return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
222 return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
225 stmt.whereCol = append(stmt.whereCol, column)
231 // parts are table|col=type,col2=type2
232 func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
234 return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
236 stmt.table = parts[0]
237 for n, colspec := range strings.Split(parts[1], ",") {
238 nameType := strings.Split(colspec, "=")
239 if len(nameType) != 2 {
240 return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
242 stmt.colName = append(stmt.colName, nameType[0])
243 stmt.colType = append(stmt.colType, nameType[1])
248 // parts are table|col=?,col2=val
249 func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
251 return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
253 stmt.table = parts[0]
254 for n, colspec := range strings.Split(parts[1], ",") {
255 nameVal := strings.Split(colspec, "=")
256 if len(nameVal) != 2 {
257 return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n)
259 column, value := nameVal[0], nameVal[1]
260 ctype, ok := c.db.columnType(stmt.table, column)
262 return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column)
264 stmt.colName = append(stmt.colName, column)
267 var subsetVal interface{}
268 // Convert to driver subset type
271 subsetVal = []byte(value)
273 i, err := strconv.Atoi(value)
275 return nil, errf("invalid conversion to int32 from %q", value)
277 subsetVal = int64(i) // int64 is a subset type, but not int32
279 return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype)
281 stmt.colValue = append(stmt.colValue, subsetVal)
284 stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
285 stmt.colValue = append(stmt.colValue, "?")
291 func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
293 panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
295 parts := strings.Split(query, "|")
297 return nil, errf("empty query")
301 stmt := &fakeStmt{q: query, c: c, cmd: cmd}
306 return c.prepareSelect(stmt, parts)
308 return c.prepareCreate(stmt, parts)
310 return c.prepareInsert(stmt, parts)
312 return nil, errf("unsupported command type %q", cmd)
317 func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
318 return s.placeholderConverter[idx]
321 func (s *fakeStmt) Close() error {
325 func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
330 return driver.DDLSuccess, nil
332 if err := db.createTable(s.table, s.colName, s.colType); err != nil {
335 return driver.DDLSuccess, nil
337 return s.execInsert(args)
339 fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s)
340 return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
343 func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
345 if len(args) != s.placeholders {
346 panic("error in pkg db; should only get here if size is correct")
349 t, ok := db.table(s.table)
352 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
358 cols := make([]interface{}, len(t.colname))
360 for n, colname := range s.colName {
361 colidx := t.columnIndex(colname)
363 return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
366 if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
375 t.rows = append(t.rows, &row{cols: cols})
376 return driver.RowsAffected(1), nil
379 func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) {
381 if len(args) != s.placeholders {
382 panic("error in pkg db; should only get here if size is correct")
386 t, ok := db.table(s.table)
389 return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
394 colIdx := make(map[string]int) // select column name -> column index in table
395 for _, name := range s.colName {
396 idx := t.columnIndex(name)
398 return nil, fmt.Errorf("fakedb: unknown column name %q", name)
405 for _, trow := range t.rows {
406 // Process the where clause, skipping non-match rows. This is lazy
407 // and just uses fmt.Sprintf("%v") to test equality. Good enough
409 for widx, wcol := range s.whereCol {
410 idx := t.columnIndex(wcol)
412 return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
414 tcol := trow.cols[idx]
415 if bs, ok := tcol.([]byte); ok {
416 // lazy hack to avoid sprintf %v on a []byte
419 if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
423 mrow := &row{cols: make([]interface{}, len(s.colName))}
424 for seli, name := range s.colName {
425 mrow.cols[seli] = trow.cols[colIdx[name]]
427 mrows = append(mrows, mrow)
430 cursor := &rowsCursor{
438 func (s *fakeStmt) NumInput() int {
439 return s.placeholders
442 func (tx *fakeTx) Commit() error {
447 func (tx *fakeTx) Rollback() error {
452 type rowsCursor struct {
459 func (rc *rowsCursor) Close() error {
464 func (rc *rowsCursor) Columns() []string {
468 func (rc *rowsCursor) Next(dest []interface{}) error {
470 return errors.New("fakedb: cursor is closed")
473 if rc.pos >= len(rc.rows) {
474 return io.EOF // per interface spec
476 for i, v := range rc.rows[rc.pos].cols {
477 // TODO(bradfitz): convert to subset types? naah, I
478 // think the subset types should only be input to
479 // driver, but the sql package should be able to handle
480 // a wider range of types coming out of drivers. all
481 // for ease of drivers, and to prevent drivers from
482 // messing up conversions or doing them differently.
488 func converterForType(typ string) driver.ValueConverter {
497 panic("invalid fakedb column type of " + typ)