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