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