Skip to content
Snippets Groups Projects
sql_test.go 69.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2011 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package sql
    
    import (
    
    	"database/sql/driver"
    
    	"math/rand"
    
    func init() {
    	type dbConn struct {
    		db *DB
    
    	}
    	freedFrom := make(map[dbConn]string)
    
    	var mu sync.Mutex
    	getFreedFrom := func(c dbConn) string {
    		mu.Lock()
    		defer mu.Unlock()
    		return freedFrom[c]
    	}
    	setFreedFrom := func(c dbConn, s string) {
    		mu.Lock()
    		defer mu.Unlock()
    		freedFrom[c] = s
    	}
    
    	putConnHook = func(db *DB, c *driverConn) {
    
    		idx := -1
    		for i, v := range db.freeConn {
    			if v == c {
    				idx = i
    				break
    			}
    		}
    		if idx >= 0 {
    
    			// print before panic, as panic may get lost due to conflicting panic
    			// (all goroutines asleep) elsewhere, since we might not unlock
    			// the mutex in freeConn here.
    
    			println("double free of conn. conflicts are:\nA) " + getFreedFrom(dbConn{db, c}) + "\n\nand\nB) " + stack())
    
    			panic("double free of conn.")
    
    		setFreedFrom(dbConn{db, c}, stack())
    
    const fakeDBName = "foo"
    
    
    var chrisBirthday = time.Unix(123456789, 0)
    
    
    func newTestDB(t testing.TB, name string) *DB {
    
    	db, err := Open("test", fakeDBName)
    
    	if err != nil {
    		t.Fatalf("Open: %v", err)
    	}
    	if _, err := db.Exec("WIPE"); err != nil {
    		t.Fatalf("exec wipe: %v", err)
    	}
    	if name == "people" {
    
    		exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime")
    
    		exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1)
    		exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2)
    
    		exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday)
    
    	if name == "magicquery" {
    		// Magic table name and column, known by fakedb_test.go.
    		exec(t, db, "CREATE|magicquery|op=string,millis=int32")
    		exec(t, db, "INSERT|magicquery|op=sleep,millis=10")
    	}
    
    func TestDriverPanic(t *testing.T) {
    	// Test that if driver panics, database/sql does not deadlock.
    	db, err := Open("test", fakeDBName)
    	if err != nil {
    		t.Fatalf("Open: %v", err)
    	}
    	expectPanic := func(name string, f func()) {
    		defer func() {
    			err := recover()
    			if err == nil {
    				t.Fatalf("%s did not panic", name)
    			}
    		}()
    		f()
    	}
    
    	expectPanic("Exec Exec", func() { db.Exec("PANIC|Exec|WIPE") })
    	exec(t, db, "WIPE") // check not deadlocked
    	expectPanic("Exec NumInput", func() { db.Exec("PANIC|NumInput|WIPE") })
    	exec(t, db, "WIPE") // check not deadlocked
    	expectPanic("Exec Close", func() { db.Exec("PANIC|Close|WIPE") })
    	exec(t, db, "WIPE")             // check not deadlocked
    	exec(t, db, "PANIC|Query|WIPE") // should run successfully: Exec does not call Query
    	exec(t, db, "WIPE")             // check not deadlocked
    
    	exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime")
    
    	expectPanic("Query Query", func() { db.Query("PANIC|Query|SELECT|people|age,name|") })
    	expectPanic("Query NumInput", func() { db.Query("PANIC|NumInput|SELECT|people|age,name|") })
    	expectPanic("Query Close", func() {
    		rows, err := db.Query("PANIC|Close|SELECT|people|age,name|")
    		if err != nil {
    			t.Fatal(err)
    		}
    		rows.Close()
    	})
    	db.Query("PANIC|Exec|SELECT|people|age,name|") // should run successfully: Query does not call Exec
    	exec(t, db, "WIPE")                            // check not deadlocked
    }
    
    
    func exec(t testing.TB, db *DB, query string, args ...interface{}) {
    
    	_, err := db.Exec(query, args...)
    	if err != nil {
    		t.Fatalf("Exec of %q: %v", query, err)
    	}
    }
    
    
    func closeDB(t testing.TB, db *DB) {
    
    	if e := recover(); e != nil {
    		fmt.Printf("Panic: %v\n", e)
    		panic(e)
    	}
    
    	defer setHookpostCloseConn(nil)
    	setHookpostCloseConn(func(_ *fakeConn, err error) {
    		if err != nil {
    			t.Errorf("Error closing fakeConn: %v", err)
    		}
    	})
    
    	for i, dc := range db.freeConn {
    
    		if n := len(dc.openStmt); n > 0 {
    			// Just a sanity check. This is legal in
    			// general, but if we make the tests clean up
    			// their statements first, then we can safely
    			// verify this is always zero here, and any
    			// other value is a leak.
    
    			t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n)
    
    	err := db.Close()
    	if err != nil {
    		t.Fatalf("error closing DB: %v", err)
    	}
    
    
    	var numOpen int
    	if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
    		numOpen = db.numOpenConns()
    		return numOpen == 0
    	}) {
    		t.Fatalf("%d connections still open after closing DB", numOpen)
    
    // numPrepares assumes that db has exactly 1 idle conn and returns
    // its count of calls to Prepare
    func numPrepares(t *testing.T, db *DB) int {
    
    	if n := len(db.freeConn); n != 1 {
    
    		t.Fatalf("free conns = %d; want 1", n)
    	}
    
    	return db.freeConn[0].ci.(*fakeConn).numPrepare
    
    func (db *DB) numDeps() int {
    	db.mu.Lock()
    	defer db.mu.Unlock()
    	return len(db.dep)
    }
    
    // Dependencies are closed via a goroutine, so this polls waiting for
    // numDeps to fall to want, waiting up to d.
    func (db *DB) numDepsPollUntil(want int, d time.Duration) int {
    	deadline := time.Now().Add(d)
    	for {
    		n := db.numDeps()
    		if n <= want || time.Now().After(deadline) {
    			return n
    		}
    		time.Sleep(50 * time.Millisecond)
    	}
    }
    
    func (db *DB) numFreeConns() int {
    	db.mu.Lock()
    	defer db.mu.Unlock()
    
    func (db *DB) numOpenConns() int {
    	db.mu.Lock()
    	defer db.mu.Unlock()
    	return db.numOpen
    }
    
    
    // clearAllConns closes all connections in db.
    func (db *DB) clearAllConns(t *testing.T) {
    	db.SetMaxIdleConns(0)
    
    	if g, w := db.numFreeConns(), 0; g != w {
    		t.Errorf("free conns = %d; want %d", g, w)
    	}
    
    	if n := db.numDepsPollUntil(0, time.Second); n > 0 {
    		t.Errorf("number of dependencies = %d; expected 0", n)
    		db.dumpDeps(t)
    	}
    }
    
    
    func (db *DB) dumpDeps(t *testing.T) {
    	for fc := range db.dep {
    		db.dumpDep(t, 0, fc, map[finalCloser]bool{})
    	}
    }
    
    func (db *DB) dumpDep(t *testing.T, depth int, dep finalCloser, seen map[finalCloser]bool) {
    	seen[dep] = true
    	indent := strings.Repeat("  ", depth)
    	ds := db.dep[dep]
    	for k := range ds {
    		t.Logf("%s%T (%p) waiting for -> %T (%p)", indent, dep, dep, k, k)
    		if fc, ok := k.(finalCloser); ok {
    			if !seen[fc] {
    				db.dumpDep(t, depth+1, fc, seen)
    			}
    		}
    	}
    }
    
    
    func TestQuery(t *testing.T) {
    
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    
    	prepares0 := numPrepares(t, db)
    
    	rows, err := db.Query("SELECT|people|age,name|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	type row struct {
    		age  int
    		name string
    	}
    	got := []row{}
    	for rows.Next() {
    		var r row
    		err = rows.Scan(&r.age, &r.name)
    		if err != nil {
    			t.Fatalf("Scan: %v", err)
    		}
    		got = append(got, r)
    	}
    	err = rows.Err()
    	if err != nil {
    		t.Fatalf("Err: %v", err)
    	}
    	want := []row{
    		{age: 1, name: "Alice"},
    		{age: 2, name: "Bob"},
    		{age: 3, name: "Chris"},
    	}
    	if !reflect.DeepEqual(got, want) {
    
    		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
    
    
    	// And verify that the final rows.Next() call, which hit EOF,
    	// also closed the rows connection.
    
    	if n := db.numFreeConns(); n != 1 {
    
    		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
    	}
    	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
    		t.Errorf("executed %d Prepare statements; want 1", prepares)
    
    // TestQueryContext tests canceling the context while scanning the rows.
    
    func TestQueryContext(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	prepares0 := numPrepares(t, db)
    
    	ctx, cancel := context.WithCancel(context.Background())
    
    
    	rows, err := db.QueryContext(ctx, "SELECT|people|age,name|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	type row struct {
    		age  int
    		name string
    	}
    	got := []row{}
    	index := 0
    	for rows.Next() {
    		if index == 2 {
    			cancel()
    
    		}
    		var r row
    		err = rows.Scan(&r.age, &r.name)
    		if err != nil {
    			if index == 2 {
    				break
    			}
    			t.Fatalf("Scan: %v", err)
    		}
    		if index == 2 && err == nil {
    			t.Fatal("expected an error on last scan")
    		}
    		got = append(got, r)
    		index++
    	}
    
    	select {
    	case <-ctx.Done():
    		if err := ctx.Err(); err != context.Canceled {
    
    			t.Fatalf("context err = %v; want context.Canceled", ctx.Err())
    
    		}
    	default:
    		t.Fatalf("context err = nil; want context.Canceled")
    
    	}
    	want := []row{
    		{age: 1, name: "Alice"},
    		{age: 2, name: "Bob"},
    	}
    	if !reflect.DeepEqual(got, want) {
    		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
    	}
    
    	// And verify that the final rows.Next() call, which hit EOF,
    	// also closed the rows connection.
    
    	waitForFree(t, db, 5*time.Second, 1)
    
    	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
    		t.Errorf("executed %d Prepare statements; want 1", prepares)
    	}
    }
    
    
    func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
    	deadline := time.Now().Add(waitFor)
    	for time.Now().Before(deadline) {
    		if fn() {
    			return true
    		}
    		time.Sleep(checkEvery)
    	}
    	return false
    }
    
    
    // waitForFree checks db.numFreeConns until either it equals want or
    // the maxWait time elapses.
    func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
    	var numFree int
    	if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
    		numFree = db.numFreeConns()
    		return numFree == want
    	}) {
    		t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
    	}
    }
    
    
    func waitForRowsClose(t *testing.T, rows *Rows, maxWait time.Duration) {
    	if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
    		rows.closemu.RLock()
    		defer rows.closemu.RUnlock()
    		return rows.closed
    	}) {
    		t.Fatal("failed to close rows")
    	}
    }
    
    // TestQueryContextWait ensures that rows and all internal statements are closed when
    // a query context is closed during execution.
    
    func TestQueryContextWait(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	prepares0 := numPrepares(t, db)
    
    
    	// TODO(kardianos): convert this from using a timeout to using an explicit
    	// cancel when the query signals that is is "executing" the query.
    	ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
    	defer cancel()
    
    
    	// This will trigger the *fakeConn.Prepare method which will take time
    	// performing the query. The ctxDriverPrepare func will check the context
    	// after this and close the rows and return an error.
    
    	_, err := db.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
    
    	if err != context.DeadlineExceeded {
    		t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
    	}
    
    	// Verify closed rows connection after error condition.
    
    	waitForFree(t, db, 5*time.Second, 1)
    
    	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
    
    		// TODO(kardianos): if the context timeouts before the db.QueryContext
    		// executes this check may fail. After adjusting how the context
    		// is canceled above revert this back to a Fatal error.
    		t.Logf("executed %d Prepare statements; want 1", prepares)
    
    // TestTxContextWait tests the transaction behavior when the tx context is canceled
    // during execution of the query.
    
    func TestTxContextWait(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    
    
    	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*15)
    	defer cancel()
    
    	tx, err := db.BeginTx(ctx, nil)
    
    		// Guard against the context being canceled before BeginTx completes.
    		if err == context.DeadlineExceeded {
    			t.Skip("tx context canceled prior to first use")
    		}
    
    		t.Fatal(err)
    	}
    
    	// This will trigger the *fakeConn.Prepare method which will take time
    	// performing the query. The ctxDriverPrepare func will check the context
    	// after this and close the rows and return an error.
    
    	_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
    
    	if err != context.DeadlineExceeded {
    		t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
    	}
    
    
    	waitForFree(t, db, 5*time.Second, 0)
    
    func TestMultiResultSetQuery(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	prepares0 := numPrepares(t, db)
    	rows, err := db.Query("SELECT|people|age,name|;SELECT|people|name|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	type row1 struct {
    		age  int
    		name string
    	}
    	type row2 struct {
    		name string
    	}
    	got1 := []row1{}
    	for rows.Next() {
    		var r row1
    		err = rows.Scan(&r.age, &r.name)
    		if err != nil {
    			t.Fatalf("Scan: %v", err)
    		}
    		got1 = append(got1, r)
    	}
    	err = rows.Err()
    	if err != nil {
    		t.Fatalf("Err: %v", err)
    	}
    	want1 := []row1{
    		{age: 1, name: "Alice"},
    		{age: 2, name: "Bob"},
    		{age: 3, name: "Chris"},
    	}
    	if !reflect.DeepEqual(got1, want1) {
    		t.Errorf("mismatch.\n got1: %#v\nwant: %#v", got1, want1)
    	}
    
    	if !rows.NextResultSet() {
    		t.Errorf("expected another result set")
    	}
    
    	got2 := []row2{}
    	for rows.Next() {
    		var r row2
    		err = rows.Scan(&r.name)
    		if err != nil {
    			t.Fatalf("Scan: %v", err)
    		}
    		got2 = append(got2, r)
    	}
    	err = rows.Err()
    	if err != nil {
    		t.Fatalf("Err: %v", err)
    	}
    	want2 := []row2{
    		{name: "Alice"},
    		{name: "Bob"},
    		{name: "Chris"},
    	}
    	if !reflect.DeepEqual(got2, want2) {
    		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got2, want2)
    	}
    	if rows.NextResultSet() {
    		t.Errorf("expected no more result sets")
    	}
    
    	// And verify that the final rows.Next() call, which hit EOF,
    	// also closed the rows connection.
    
    	waitForFree(t, db, 5*time.Second, 1)
    
    	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
    		t.Errorf("executed %d Prepare statements; want 1", prepares)
    	}
    }
    
    
    func TestQueryNamedArg(t *testing.T) {
    
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	prepares0 := numPrepares(t, db)
    	rows, err := db.Query(
    		// Ensure the name and age parameters only match on placeholder name, not position.
    		"SELECT|people|age,name|name=?name,age=?age",
    
    		Named("age", 2),
    		Named("name", "Bob"),
    
    	)
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	type row struct {
    		age  int
    		name string
    	}
    	got := []row{}
    	for rows.Next() {
    		var r row
    		err = rows.Scan(&r.age, &r.name)
    		if err != nil {
    			t.Fatalf("Scan: %v", err)
    		}
    		got = append(got, r)
    	}
    	err = rows.Err()
    	if err != nil {
    		t.Fatalf("Err: %v", err)
    	}
    	want := []row{
    		{age: 2, name: "Bob"},
    	}
    	if !reflect.DeepEqual(got, want) {
    		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
    	}
    
    	// And verify that the final rows.Next() call, which hit EOF,
    	// also closed the rows connection.
    	if n := db.numFreeConns(); n != 1 {
    		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
    	}
    	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
    		t.Errorf("executed %d Prepare statements; want 1", prepares)
    	}
    }
    
    
    func TestPoolExhaustOnCancel(t *testing.T) {
    	if testing.Short() {
    		t.Skip("long test")
    	}
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    
    	max := 3
    
    	db.SetMaxOpenConns(max)
    
    	// First saturate the connection pool.
    	// Then start new requests for a connection that is cancelled after it is requested.
    
    	var saturate, saturateDone sync.WaitGroup
    	saturate.Add(max)
    	saturateDone.Add(max)
    
    	for i := 0; i < max; i++ {
    		go func() {
    			saturate.Done()
    			rows, err := db.Query("WAIT|500ms|SELECT|people|name,photo|")
    			if err != nil {
    				t.Fatalf("Query: %v", err)
    			}
    			rows.Close()
    			saturateDone.Done()
    		}()
    	}
    
    	saturate.Wait()
    
    	// Now cancel the request while it is waiting.
    	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
    	defer cancel()
    
    	for i := 0; i < max; i++ {
    		ctxReq, cancelReq := context.WithCancel(ctx)
    		go func() {
    			time.Sleep(time.Millisecond * 100)
    			cancelReq()
    		}()
    		err := db.PingContext(ctxReq)
    		if err != context.Canceled {
    			t.Fatalf("PingContext (Exhaust): %v", err)
    		}
    	}
    
    	saturateDone.Wait()
    
    	// Now try to open a normal connection.
    	err := db.PingContext(ctx)
    	if err != nil {
    		t.Fatalf("PingContext (Normal): %v", err)
    	}
    }
    
    
    func TestByteOwnership(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	rows, err := db.Query("SELECT|people|name,photo|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	type row struct {
    		name  []byte
    		photo RawBytes
    	}
    	got := []row{}
    	for rows.Next() {
    		var r row
    		err = rows.Scan(&r.name, &r.photo)
    		if err != nil {
    			t.Fatalf("Scan: %v", err)
    		}
    		got = append(got, r)
    	}
    	corruptMemory := []byte("\xffPHOTO")
    	want := []row{
    		{name: []byte("Alice"), photo: corruptMemory},
    		{name: []byte("Bob"), photo: corruptMemory},
    		{name: []byte("Chris"), photo: corruptMemory},
    	}
    	if !reflect.DeepEqual(got, want) {
    		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
    	}
    
    	var photo RawBytes
    	err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo)
    	if err == nil {
    		t.Error("want error scanning into RawBytes from QueryRow")
    	}
    }
    
    
    Brad Fitzpatrick's avatar
    Brad Fitzpatrick committed
    func TestRowsColumns(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	rows, err := db.Query("SELECT|people|age,name|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	cols, err := rows.Columns()
    	if err != nil {
    		t.Fatalf("Columns: %v", err)
    	}
    	want := []string{"age", "name"}
    	if !reflect.DeepEqual(cols, want) {
    		t.Errorf("got %#v; want %#v", cols, want)
    	}
    
    	if err := rows.Close(); err != nil {
    		t.Errorf("error closing rows: %s", err)
    	}
    
    func TestRowsColumnTypes(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	rows, err := db.Query("SELECT|people|age,name|")
    	if err != nil {
    		t.Fatalf("Query: %v", err)
    	}
    	tt, err := rows.ColumnTypes()
    	if err != nil {
    		t.Fatalf("ColumnTypes: %v", err)
    	}
    
    	types := make([]reflect.Type, len(tt))
    	for i, tp := range tt {
    		st := tp.ScanType()
    		if st == nil {
    			t.Errorf("scantype is null for column %q", tp.Name())
    			continue
    		}
    		types[i] = st
    	}
    	values := make([]interface{}, len(tt))
    	for i := range values {
    		values[i] = reflect.New(types[i]).Interface()
    	}
    	ct := 0
    	for rows.Next() {
    		err = rows.Scan(values...)
    		if err != nil {
    			t.Fatalf("failed to scan values in %v", err)
    		}
    		ct++
    		if ct == 0 {
    			if values[0].(string) != "Bob" {
    				t.Errorf("Expected Bob, got %v", values[0])
    			}
    			if values[1].(int) != 2 {
    				t.Errorf("Expected 2, got %v", values[1])
    			}
    		}
    	}
    	if ct != 3 {
    		t.Errorf("expected 3 rows, got %d", ct)
    	}
    
    	if err := rows.Close(); err != nil {
    		t.Errorf("error closing rows: %s", err)
    	}
    }
    
    
    func TestQueryRow(t *testing.T) {
    
    	db := newTestDB(t, "people")
    
    	var name string
    	var age int
    
    	var birthday time.Time
    
    
    	err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age)
    
    	if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") {
    
    		t.Errorf("expected error from wrong number of arguments; actually got: %v", err)
    	}
    
    
    	err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday)
    	if err != nil || !birthday.Equal(chrisBirthday) {
    		t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday)
    	}
    
    
    	err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name)
    	if err != nil {
    		t.Fatalf("age QueryRow+Scan: %v", err)
    	}
    	if name != "Bob" {
    		t.Errorf("expected name Bob, got %q", name)
    	}
    	if age != 2 {
    		t.Errorf("expected age 2, got %d", age)
    	}
    
    	err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name)
    	if err != nil {
    		t.Fatalf("name QueryRow+Scan: %v", err)
    	}
    	if name != "Alice" {
    		t.Errorf("expected name Alice, got %q", name)
    	}
    	if age != 1 {
    		t.Errorf("expected age 1, got %d", age)
    	}
    
    
    	var photo []byte
    	err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo)
    	if err != nil {
    		t.Fatalf("photo QueryRow+Scan: %v", err)
    	}
    	want := []byte("APHOTO")
    	if !reflect.DeepEqual(photo, want) {
    		t.Errorf("photo = %q; want %q", photo, want)
    	}
    
    func TestTxRollbackCommitErr(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    
    	tx, err := db.Begin()
    	if err != nil {
    		t.Fatal(err)
    	}
    	err = tx.Rollback()
    	if err != nil {
    		t.Errorf("expected nil error from Rollback; got %v", err)
    	}
    	err = tx.Commit()
    	if err != ErrTxDone {
    		t.Errorf("expected %q from Commit; got %q", ErrTxDone, err)
    	}
    
    	tx, err = db.Begin()
    	if err != nil {
    		t.Fatal(err)
    	}
    	err = tx.Commit()
    	if err != nil {
    		t.Errorf("expected nil error from Commit; got %v", err)
    	}
    	err = tx.Rollback()
    	if err != ErrTxDone {
    		t.Errorf("expected %q from Rollback; got %q", ErrTxDone, err)
    	}
    }
    
    
    func TestStatementErrorAfterClose(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	stmt, err := db.Prepare("SELECT|people|age|name=?")
    	if err != nil {
    		t.Fatalf("Prepare: %v", err)
    	}
    	err = stmt.Close()
    	if err != nil {
    		t.Fatalf("Close: %v", err)
    	}
    	var name string
    	err = stmt.QueryRow("foo").Scan(&name)
    	if err == nil {
    		t.Errorf("expected error from QueryRow.Scan after Stmt.Close")
    	}
    }
    
    
    func TestStatementQueryRow(t *testing.T) {
    	db := newTestDB(t, "people")
    
    	stmt, err := db.Prepare("SELECT|people|age|name=?")
    	if err != nil {
    		t.Fatalf("Prepare: %v", err)
    	}
    
    	var age int
    	for n, tt := range []struct {
    		name string
    		want int
    	}{
    		{"Alice", 1},
    		{"Bob", 2},
    		{"Chris", 3},
    	} {
    		if err := stmt.QueryRow(tt.name).Scan(&age); err != nil {
    			t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err)
    		} else if age != tt.want {
    
    			t.Errorf("%d: age=%d, want %d", n, age, tt.want)
    
    type stubDriverStmt struct {
    	err error
    }
    
    func (s stubDriverStmt) Close() error {
    	return s.err
    }
    
    func (s stubDriverStmt) NumInput() int {
    	return -1
    }
    
    func (s stubDriverStmt) Exec(args []driver.Value) (driver.Result, error) {
    	return nil, nil
    }
    
    func (s stubDriverStmt) Query(args []driver.Value) (driver.Rows, error) {
    	return nil, nil
    }
    
    // golang.org/issue/12798
    func TestStatementClose(t *testing.T) {
    	want := errors.New("STMT ERROR")
    
    	tests := []struct {
    		stmt *Stmt
    		msg  string
    	}{
    		{&Stmt{stickyErr: want}, "stickyErr not propagated"},
    
    		{&Stmt{tx: &Tx{}, txds: &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{want}}}, "driverStmt.Close() error not propagated"},
    
    	}
    	for _, test := range tests {
    		if err := test.stmt.Close(); err != want {
    			t.Errorf("%s. Got stmt.Close() = %v, want = %v", test.msg, err, want)
    		}
    	}
    }
    
    
    // golang.org/issue/3734
    func TestStatementQueryRowConcurrent(t *testing.T) {
    	db := newTestDB(t, "people")
    	defer closeDB(t, db)
    	stmt, err := db.Prepare("SELECT|people|age|name=?")
    	if err != nil {
    		t.Fatalf("Prepare: %v", err)
    	}
    	defer stmt.Close()
    
    	const n = 10
    	ch := make(chan error, n)
    	for i := 0; i < n; i++ {
    		go func() {
    			var age int
    			err := stmt.QueryRow("Alice").Scan(&age)
    			if err == nil && age != 1 {
    				err = fmt.Errorf("unexpected age %d", age)
    			}
    			ch <- err
    		}()
    	}
    	for i := 0; i < n; i++ {
    		if err := <-ch; err != nil {
    			t.Error(err)
    		}
    	}
    }
    
    
    // just a test of fakedb itself
    func TestBogusPreboundParameters(t *testing.T) {
    	db := newTestDB(t, "foo")
    
    	exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
    	_, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion")
    	if err == nil {
    		t.Fatalf("expected error")
    	}
    
    	if err.Error() != `fakedb: invalid conversion to int32 from "bogusconversion"` {
    
    		t.Errorf("unexpected error: %v", err)
    	}
    }
    
    
    	db := newTestDB(t, "foo")
    
    	exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
    	stmt, err := db.Prepare("INSERT|t1|name=?,age=?")
    	if err != nil {
    		t.Errorf("Stmt, err = %v, %v", stmt, err)
    	}
    
    
    	type execTest struct {
    		args    []interface{}
    		wantErr string
    	}
    	execTests := []execTest{
    		// Okay:
    		{[]interface{}{"Brad", 31}, ""},
    		{[]interface{}{"Brad", int64(31)}, ""},
    		{[]interface{}{"Bob", "32"}, ""},
    		{[]interface{}{7, 9}, ""},
    
    		// Invalid conversions:
    
    		{[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument $2 type: sql/driver: value 4294967295 overflows int32"},
    		{[]interface{}{"Brad", "strconv fail"}, `sql: converting argument $2 type: sql/driver: value "strconv fail" can't be converted to int32`},
    
    
    		// Wrong number of args:
    
    Brad Fitzpatrick's avatar
    Brad Fitzpatrick committed
    		{[]interface{}{}, "sql: expected 2 arguments, got 0"},
    		{[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"},
    
    	}
    	for n, et := range execTests {
    		_, err := stmt.Exec(et.args...)
    		errStr := ""
    		if err != nil {
    
    			errStr = err.Error()
    
    		}
    		if errStr != et.wantErr {
    			t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q",
    				n, et.args, errStr, et.wantErr)
    		}
    	}
    }
    
    func TestTxPrepare(t *testing.T) {
    	db := newTestDB(t, "")
    	defer closeDB(t, db)
    	exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool")
    	tx, err := db.Begin()
    	if err != nil {
    		t.Fatalf("Begin = %v", err)
    	}
    	stmt, err := tx.Prepare("INSERT|t1|name=?,age=?")
    	if err != nil {
    		t.Fatalf("Stmt, err = %v, %v", stmt, err)
    	}
    	defer stmt.Close()
    	_, err = stmt.Exec("Bobby", 7)
    	if err != nil {
    		t.Fatalf("Exec = %v", err)
    	}
    	err = tx.Commit()
    	if err != nil {
    		t.Fatalf("Commit = %v", err)
    	}
    	// Commit() should have closed the statement
    	if !stmt.closed {
    		t.Fatal("Stmt not closed after Commit")
    	}
    }
    
    
    func TestTxStmt(t *testing.T) {
    	db := newTestDB(t, "")
    	defer closeDB(t, db)