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