Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 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 ( |
Brad Fitzpatrick | ca3ed9f | 2013-08-13 14:56:40 -0700 | [diff] [blame] | 8 | "database/sql/driver" |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 9 | "errors" |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 10 | "fmt" |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 11 | "math/rand" |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 12 | "reflect" |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 13 | "runtime" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 14 | "strings" |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 15 | "sync" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 16 | "testing" |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 17 | "time" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 18 | ) |
| 19 | |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 20 | func init() { |
| 21 | type dbConn struct { |
| 22 | db *DB |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 23 | c *driverConn |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 24 | } |
| 25 | freedFrom := make(map[dbConn]string) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 26 | putConnHook = func(db *DB, c *driverConn) { |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 27 | idx := -1 |
| 28 | for i, v := range db.freeConn { |
| 29 | if v == c { |
| 30 | idx = i |
| 31 | break |
| 32 | } |
| 33 | } |
| 34 | if idx >= 0 { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 35 | // print before panic, as panic may get lost due to conflicting panic |
| 36 | // (all goroutines asleep) elsewhere, since we might not unlock |
| 37 | // the mutex in freeConn here. |
| 38 | println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) |
| 39 | panic("double free of conn.") |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 40 | } |
| 41 | freedFrom[dbConn{db, c}] = stack() |
| 42 | } |
| 43 | } |
| 44 | |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 45 | const fakeDBName = "foo" |
| 46 | |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 47 | var chrisBirthday = time.Unix(123456789, 0) |
| 48 | |
Brad Fitzpatrick | 35d8bb3 | 2013-08-14 23:21:32 -0700 | [diff] [blame] | 49 | func newTestDB(t testing.TB, name string) *DB { |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 50 | db, err := Open("test", fakeDBName) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 51 | if err != nil { |
| 52 | t.Fatalf("Open: %v", err) |
| 53 | } |
| 54 | if _, err := db.Exec("WIPE"); err != nil { |
| 55 | t.Fatalf("exec wipe: %v", err) |
| 56 | } |
| 57 | if name == "people" { |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 58 | exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime") |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 59 | exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1) |
| 60 | exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2) |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 61 | exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 62 | } |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 63 | if name == "magicquery" { |
| 64 | // Magic table name and column, known by fakedb_test.go. |
| 65 | exec(t, db, "CREATE|magicquery|op=string,millis=int32") |
| 66 | exec(t, db, "INSERT|magicquery|op=sleep,millis=10") |
| 67 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 68 | return db |
| 69 | } |
| 70 | |
Russ Cox | 99ed71a | 2015-12-18 12:16:05 -0500 | [diff] [blame] | 71 | func TestDriverPanic(t *testing.T) { |
| 72 | // Test that if driver panics, database/sql does not deadlock. |
| 73 | db, err := Open("test", fakeDBName) |
| 74 | if err != nil { |
| 75 | t.Fatalf("Open: %v", err) |
| 76 | } |
| 77 | expectPanic := func(name string, f func()) { |
| 78 | defer func() { |
| 79 | err := recover() |
| 80 | if err == nil { |
| 81 | t.Fatalf("%s did not panic", name) |
| 82 | } |
| 83 | }() |
| 84 | f() |
| 85 | } |
| 86 | |
| 87 | expectPanic("Exec Exec", func() { db.Exec("PANIC|Exec|WIPE") }) |
| 88 | exec(t, db, "WIPE") // check not deadlocked |
| 89 | expectPanic("Exec NumInput", func() { db.Exec("PANIC|NumInput|WIPE") }) |
| 90 | exec(t, db, "WIPE") // check not deadlocked |
| 91 | expectPanic("Exec Close", func() { db.Exec("PANIC|Close|WIPE") }) |
| 92 | exec(t, db, "WIPE") // check not deadlocked |
| 93 | exec(t, db, "PANIC|Query|WIPE") // should run successfully: Exec does not call Query |
| 94 | exec(t, db, "WIPE") // check not deadlocked |
| 95 | |
| 96 | exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime") |
| 97 | |
| 98 | expectPanic("Query Query", func() { db.Query("PANIC|Query|SELECT|people|age,name|") }) |
| 99 | expectPanic("Query NumInput", func() { db.Query("PANIC|NumInput|SELECT|people|age,name|") }) |
| 100 | expectPanic("Query Close", func() { |
| 101 | rows, err := db.Query("PANIC|Close|SELECT|people|age,name|") |
| 102 | if err != nil { |
| 103 | t.Fatal(err) |
| 104 | } |
| 105 | rows.Close() |
| 106 | }) |
| 107 | db.Query("PANIC|Exec|SELECT|people|age,name|") // should run successfully: Query does not call Exec |
| 108 | exec(t, db, "WIPE") // check not deadlocked |
| 109 | } |
| 110 | |
Brad Fitzpatrick | 35d8bb3 | 2013-08-14 23:21:32 -0700 | [diff] [blame] | 111 | func exec(t testing.TB, db *DB, query string, args ...interface{}) { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 112 | _, err := db.Exec(query, args...) |
| 113 | if err != nil { |
| 114 | t.Fatalf("Exec of %q: %v", query, err) |
| 115 | } |
| 116 | } |
| 117 | |
Brad Fitzpatrick | 35d8bb3 | 2013-08-14 23:21:32 -0700 | [diff] [blame] | 118 | func closeDB(t testing.TB, db *DB) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 119 | if e := recover(); e != nil { |
| 120 | fmt.Printf("Panic: %v\n", e) |
| 121 | panic(e) |
| 122 | } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 123 | defer setHookpostCloseConn(nil) |
| 124 | setHookpostCloseConn(func(_ *fakeConn, err error) { |
| 125 | if err != nil { |
| 126 | t.Errorf("Error closing fakeConn: %v", err) |
| 127 | } |
| 128 | }) |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 129 | for i, dc := range db.freeConn { |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 130 | if n := len(dc.openStmt); n > 0 { |
| 131 | // Just a sanity check. This is legal in |
| 132 | // general, but if we make the tests clean up |
| 133 | // their statements first, then we can safely |
| 134 | // verify this is always zero here, and any |
| 135 | // other value is a leak. |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 136 | t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n) |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 137 | } |
| 138 | } |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 139 | err := db.Close() |
| 140 | if err != nil { |
| 141 | t.Fatalf("error closing DB: %v", err) |
| 142 | } |
Alberto García Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 143 | db.mu.Lock() |
| 144 | count := db.numOpen |
| 145 | db.mu.Unlock() |
| 146 | if count != 0 { |
| 147 | t.Fatalf("%d connections still open after closing DB", db.numOpen) |
| 148 | } |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 149 | } |
| 150 | |
Brad Fitzpatrick | 48eacd9 | 2012-03-06 14:10:58 -0800 | [diff] [blame] | 151 | // numPrepares assumes that db has exactly 1 idle conn and returns |
| 152 | // its count of calls to Prepare |
| 153 | func numPrepares(t *testing.T, db *DB) int { |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 154 | if n := len(db.freeConn); n != 1 { |
Brad Fitzpatrick | 48eacd9 | 2012-03-06 14:10:58 -0800 | [diff] [blame] | 155 | t.Fatalf("free conns = %d; want 1", n) |
| 156 | } |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 157 | return db.freeConn[0].ci.(*fakeConn).numPrepare |
Brad Fitzpatrick | 48eacd9 | 2012-03-06 14:10:58 -0800 | [diff] [blame] | 158 | } |
| 159 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 160 | func (db *DB) numDeps() int { |
| 161 | db.mu.Lock() |
| 162 | defer db.mu.Unlock() |
| 163 | return len(db.dep) |
| 164 | } |
| 165 | |
| 166 | // Dependencies are closed via a goroutine, so this polls waiting for |
| 167 | // numDeps to fall to want, waiting up to d. |
| 168 | func (db *DB) numDepsPollUntil(want int, d time.Duration) int { |
| 169 | deadline := time.Now().Add(d) |
| 170 | for { |
| 171 | n := db.numDeps() |
| 172 | if n <= want || time.Now().After(deadline) { |
| 173 | return n |
| 174 | } |
| 175 | time.Sleep(50 * time.Millisecond) |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | func (db *DB) numFreeConns() int { |
| 180 | db.mu.Lock() |
| 181 | defer db.mu.Unlock() |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 182 | return len(db.freeConn) |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 183 | } |
| 184 | |
INADA Naoki | 0c516c1 | 2015-03-03 21:27:07 +0900 | [diff] [blame] | 185 | // clearAllConns closes all connections in db. |
| 186 | func (db *DB) clearAllConns(t *testing.T) { |
| 187 | db.SetMaxIdleConns(0) |
| 188 | |
| 189 | if g, w := db.numFreeConns(), 0; g != w { |
| 190 | t.Errorf("free conns = %d; want %d", g, w) |
| 191 | } |
| 192 | |
| 193 | if n := db.numDepsPollUntil(0, time.Second); n > 0 { |
| 194 | t.Errorf("number of dependencies = %d; expected 0", n) |
| 195 | db.dumpDeps(t) |
| 196 | } |
| 197 | } |
| 198 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 199 | func (db *DB) dumpDeps(t *testing.T) { |
| 200 | for fc := range db.dep { |
| 201 | db.dumpDep(t, 0, fc, map[finalCloser]bool{}) |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | func (db *DB) dumpDep(t *testing.T, depth int, dep finalCloser, seen map[finalCloser]bool) { |
| 206 | seen[dep] = true |
| 207 | indent := strings.Repeat(" ", depth) |
| 208 | ds := db.dep[dep] |
| 209 | for k := range ds { |
| 210 | t.Logf("%s%T (%p) waiting for -> %T (%p)", indent, dep, dep, k, k) |
| 211 | if fc, ok := k.(finalCloser); ok { |
| 212 | if !seen[fc] { |
| 213 | db.dumpDep(t, depth+1, fc, seen) |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 219 | func TestQuery(t *testing.T) { |
| 220 | db := newTestDB(t, "people") |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 221 | defer closeDB(t, db) |
Brad Fitzpatrick | 48eacd9 | 2012-03-06 14:10:58 -0800 | [diff] [blame] | 222 | prepares0 := numPrepares(t, db) |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 223 | rows, err := db.Query("SELECT|people|age,name|") |
| 224 | if err != nil { |
| 225 | t.Fatalf("Query: %v", err) |
| 226 | } |
| 227 | type row struct { |
| 228 | age int |
| 229 | name string |
| 230 | } |
| 231 | got := []row{} |
| 232 | for rows.Next() { |
| 233 | var r row |
| 234 | err = rows.Scan(&r.age, &r.name) |
| 235 | if err != nil { |
| 236 | t.Fatalf("Scan: %v", err) |
| 237 | } |
| 238 | got = append(got, r) |
| 239 | } |
| 240 | err = rows.Err() |
| 241 | if err != nil { |
| 242 | t.Fatalf("Err: %v", err) |
| 243 | } |
| 244 | want := []row{ |
| 245 | {age: 1, name: "Alice"}, |
| 246 | {age: 2, name: "Bob"}, |
| 247 | {age: 3, name: "Chris"}, |
| 248 | } |
| 249 | if !reflect.DeepEqual(got, want) { |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 250 | t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 251 | } |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 252 | |
| 253 | // And verify that the final rows.Next() call, which hit EOF, |
| 254 | // also closed the rows connection. |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 255 | if n := db.numFreeConns(); n != 1 { |
Brad Fitzpatrick | 48eacd9 | 2012-03-06 14:10:58 -0800 | [diff] [blame] | 256 | t.Fatalf("free conns after query hitting EOF = %d; want 1", n) |
| 257 | } |
| 258 | if prepares := numPrepares(t, db) - prepares0; prepares != 1 { |
| 259 | t.Errorf("executed %d Prepare statements; want 1", prepares) |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 260 | } |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 261 | } |
| 262 | |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 263 | func TestByteOwnership(t *testing.T) { |
| 264 | db := newTestDB(t, "people") |
| 265 | defer closeDB(t, db) |
| 266 | rows, err := db.Query("SELECT|people|name,photo|") |
| 267 | if err != nil { |
| 268 | t.Fatalf("Query: %v", err) |
| 269 | } |
| 270 | type row struct { |
| 271 | name []byte |
| 272 | photo RawBytes |
| 273 | } |
| 274 | got := []row{} |
| 275 | for rows.Next() { |
| 276 | var r row |
| 277 | err = rows.Scan(&r.name, &r.photo) |
| 278 | if err != nil { |
| 279 | t.Fatalf("Scan: %v", err) |
| 280 | } |
| 281 | got = append(got, r) |
| 282 | } |
| 283 | corruptMemory := []byte("\xffPHOTO") |
| 284 | want := []row{ |
| 285 | {name: []byte("Alice"), photo: corruptMemory}, |
| 286 | {name: []byte("Bob"), photo: corruptMemory}, |
| 287 | {name: []byte("Chris"), photo: corruptMemory}, |
| 288 | } |
| 289 | if !reflect.DeepEqual(got, want) { |
| 290 | t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) |
| 291 | } |
| 292 | |
| 293 | var photo RawBytes |
| 294 | err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) |
| 295 | if err == nil { |
| 296 | t.Error("want error scanning into RawBytes from QueryRow") |
| 297 | } |
| 298 | } |
| 299 | |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 300 | func TestRowsColumns(t *testing.T) { |
| 301 | db := newTestDB(t, "people") |
| 302 | defer closeDB(t, db) |
| 303 | rows, err := db.Query("SELECT|people|age,name|") |
| 304 | if err != nil { |
| 305 | t.Fatalf("Query: %v", err) |
| 306 | } |
| 307 | cols, err := rows.Columns() |
| 308 | if err != nil { |
| 309 | t.Fatalf("Columns: %v", err) |
| 310 | } |
| 311 | want := []string{"age", "name"} |
| 312 | if !reflect.DeepEqual(cols, want) { |
| 313 | t.Errorf("got %#v; want %#v", cols, want) |
| 314 | } |
Alberto García Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 315 | if err := rows.Close(); err != nil { |
| 316 | t.Errorf("error closing rows: %s", err) |
| 317 | } |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 318 | } |
| 319 | |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 320 | func TestQueryRow(t *testing.T) { |
| 321 | db := newTestDB(t, "people") |
| 322 | defer closeDB(t, db) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 323 | var name string |
| 324 | var age int |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 325 | var birthday time.Time |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 326 | |
| 327 | err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 328 | if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 329 | t.Errorf("expected error from wrong number of arguments; actually got: %v", err) |
| 330 | } |
| 331 | |
Brad Fitzpatrick | bf734d6 | 2012-01-13 15:45:05 -0800 | [diff] [blame] | 332 | err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday) |
| 333 | if err != nil || !birthday.Equal(chrisBirthday) { |
| 334 | t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday) |
| 335 | } |
| 336 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 337 | err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) |
| 338 | if err != nil { |
| 339 | t.Fatalf("age QueryRow+Scan: %v", err) |
| 340 | } |
| 341 | if name != "Bob" { |
| 342 | t.Errorf("expected name Bob, got %q", name) |
| 343 | } |
| 344 | if age != 2 { |
| 345 | t.Errorf("expected age 2, got %d", age) |
| 346 | } |
| 347 | |
| 348 | err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name) |
| 349 | if err != nil { |
| 350 | t.Fatalf("name QueryRow+Scan: %v", err) |
| 351 | } |
| 352 | if name != "Alice" { |
| 353 | t.Errorf("expected name Alice, got %q", name) |
| 354 | } |
| 355 | if age != 1 { |
| 356 | t.Errorf("expected age 1, got %d", age) |
| 357 | } |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 358 | |
| 359 | var photo []byte |
| 360 | err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) |
| 361 | if err != nil { |
| 362 | t.Fatalf("photo QueryRow+Scan: %v", err) |
| 363 | } |
| 364 | want := []byte("APHOTO") |
| 365 | if !reflect.DeepEqual(photo, want) { |
| 366 | t.Errorf("photo = %q; want %q", photo, want) |
| 367 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 368 | } |
| 369 | |
Brad Fitzpatrick | 750d0e3 | 2011-11-20 14:56:49 -0500 | [diff] [blame] | 370 | func TestStatementErrorAfterClose(t *testing.T) { |
| 371 | db := newTestDB(t, "people") |
| 372 | defer closeDB(t, db) |
| 373 | stmt, err := db.Prepare("SELECT|people|age|name=?") |
| 374 | if err != nil { |
| 375 | t.Fatalf("Prepare: %v", err) |
| 376 | } |
| 377 | err = stmt.Close() |
| 378 | if err != nil { |
| 379 | t.Fatalf("Close: %v", err) |
| 380 | } |
| 381 | var name string |
| 382 | err = stmt.QueryRow("foo").Scan(&name) |
| 383 | if err == nil { |
| 384 | t.Errorf("expected error from QueryRow.Scan after Stmt.Close") |
| 385 | } |
| 386 | } |
| 387 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 388 | func TestStatementQueryRow(t *testing.T) { |
| 389 | db := newTestDB(t, "people") |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 390 | defer closeDB(t, db) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 391 | stmt, err := db.Prepare("SELECT|people|age|name=?") |
| 392 | if err != nil { |
| 393 | t.Fatalf("Prepare: %v", err) |
| 394 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 395 | defer stmt.Close() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 396 | var age int |
| 397 | for n, tt := range []struct { |
| 398 | name string |
| 399 | want int |
| 400 | }{ |
| 401 | {"Alice", 1}, |
| 402 | {"Bob", 2}, |
| 403 | {"Chris", 3}, |
| 404 | } { |
| 405 | if err := stmt.QueryRow(tt.name).Scan(&age); err != nil { |
| 406 | t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err) |
| 407 | } else if age != tt.want { |
Robert Hencke | c501824 | 2011-10-13 13:34:01 +1100 | [diff] [blame] | 408 | t.Errorf("%d: age=%d, want %d", n, age, tt.want) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 409 | } |
| 410 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 411 | } |
| 412 | |
Ian Gudger | 73fe6123 | 2015-10-01 03:29:27 -0700 | [diff] [blame] | 413 | type stubDriverStmt struct { |
| 414 | err error |
| 415 | } |
| 416 | |
| 417 | func (s stubDriverStmt) Close() error { |
| 418 | return s.err |
| 419 | } |
| 420 | |
| 421 | func (s stubDriverStmt) NumInput() int { |
| 422 | return -1 |
| 423 | } |
| 424 | |
| 425 | func (s stubDriverStmt) Exec(args []driver.Value) (driver.Result, error) { |
| 426 | return nil, nil |
| 427 | } |
| 428 | |
| 429 | func (s stubDriverStmt) Query(args []driver.Value) (driver.Rows, error) { |
| 430 | return nil, nil |
| 431 | } |
| 432 | |
| 433 | // golang.org/issue/12798 |
| 434 | func TestStatementClose(t *testing.T) { |
| 435 | want := errors.New("STMT ERROR") |
| 436 | |
| 437 | tests := []struct { |
| 438 | stmt *Stmt |
| 439 | msg string |
| 440 | }{ |
| 441 | {&Stmt{stickyErr: want}, "stickyErr not propagated"}, |
| 442 | {&Stmt{tx: &Tx{}, txsi: &driverStmt{&sync.Mutex{}, stubDriverStmt{want}}}, "driverStmt.Close() error not propagated"}, |
| 443 | } |
| 444 | for _, test := range tests { |
| 445 | if err := test.stmt.Close(); err != want { |
| 446 | t.Errorf("%s. Got stmt.Close() = %v, want = %v", test.msg, err, want) |
| 447 | } |
| 448 | } |
| 449 | } |
| 450 | |
Brad Fitzpatrick | c53fab9 | 2013-02-20 22:15:36 -0800 | [diff] [blame] | 451 | // golang.org/issue/3734 |
| 452 | func TestStatementQueryRowConcurrent(t *testing.T) { |
| 453 | db := newTestDB(t, "people") |
| 454 | defer closeDB(t, db) |
| 455 | stmt, err := db.Prepare("SELECT|people|age|name=?") |
| 456 | if err != nil { |
| 457 | t.Fatalf("Prepare: %v", err) |
| 458 | } |
| 459 | defer stmt.Close() |
| 460 | |
| 461 | const n = 10 |
| 462 | ch := make(chan error, n) |
| 463 | for i := 0; i < n; i++ { |
| 464 | go func() { |
| 465 | var age int |
| 466 | err := stmt.QueryRow("Alice").Scan(&age) |
| 467 | if err == nil && age != 1 { |
| 468 | err = fmt.Errorf("unexpected age %d", age) |
| 469 | } |
| 470 | ch <- err |
| 471 | }() |
| 472 | } |
| 473 | for i := 0; i < n; i++ { |
| 474 | if err := <-ch; err != nil { |
| 475 | t.Error(err) |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 480 | // just a test of fakedb itself |
| 481 | func TestBogusPreboundParameters(t *testing.T) { |
| 482 | db := newTestDB(t, "foo") |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 483 | defer closeDB(t, db) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 484 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 485 | _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") |
| 486 | if err == nil { |
| 487 | t.Fatalf("expected error") |
| 488 | } |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 489 | if err.Error() != `fakedb: invalid conversion to int32 from "bogusconversion"` { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 490 | t.Errorf("unexpected error: %v", err) |
| 491 | } |
| 492 | } |
| 493 | |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 494 | func TestExec(t *testing.T) { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 495 | db := newTestDB(t, "foo") |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 496 | defer closeDB(t, db) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 497 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 498 | stmt, err := db.Prepare("INSERT|t1|name=?,age=?") |
| 499 | if err != nil { |
| 500 | t.Errorf("Stmt, err = %v, %v", stmt, err) |
| 501 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 502 | defer stmt.Close() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 503 | |
| 504 | type execTest struct { |
| 505 | args []interface{} |
| 506 | wantErr string |
| 507 | } |
| 508 | execTests := []execTest{ |
| 509 | // Okay: |
| 510 | {[]interface{}{"Brad", 31}, ""}, |
| 511 | {[]interface{}{"Brad", int64(31)}, ""}, |
| 512 | {[]interface{}{"Bob", "32"}, ""}, |
| 513 | {[]interface{}{7, 9}, ""}, |
| 514 | |
| 515 | // Invalid conversions: |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 516 | {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument #1's type: sql/driver: value 4294967295 overflows int32"}, |
| 517 | {[]interface{}{"Brad", "strconv fail"}, "sql: converting argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 518 | |
| 519 | // Wrong number of args: |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 520 | {[]interface{}{}, "sql: expected 2 arguments, got 0"}, |
| 521 | {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"}, |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 522 | } |
| 523 | for n, et := range execTests { |
| 524 | _, err := stmt.Exec(et.args...) |
| 525 | errStr := "" |
| 526 | if err != nil { |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 527 | errStr = err.Error() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 528 | } |
| 529 | if errStr != et.wantErr { |
| 530 | t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q", |
| 531 | n, et.args, errStr, et.wantErr) |
| 532 | } |
| 533 | } |
| 534 | } |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 535 | |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 536 | func TestTxPrepare(t *testing.T) { |
| 537 | db := newTestDB(t, "") |
| 538 | defer closeDB(t, db) |
| 539 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 540 | tx, err := db.Begin() |
| 541 | if err != nil { |
| 542 | t.Fatalf("Begin = %v", err) |
| 543 | } |
| 544 | stmt, err := tx.Prepare("INSERT|t1|name=?,age=?") |
| 545 | if err != nil { |
| 546 | t.Fatalf("Stmt, err = %v, %v", stmt, err) |
| 547 | } |
| 548 | defer stmt.Close() |
| 549 | _, err = stmt.Exec("Bobby", 7) |
| 550 | if err != nil { |
| 551 | t.Fatalf("Exec = %v", err) |
| 552 | } |
| 553 | err = tx.Commit() |
| 554 | if err != nil { |
| 555 | t.Fatalf("Commit = %v", err) |
| 556 | } |
| 557 | // Commit() should have closed the statement |
| 558 | if !stmt.closed { |
| 559 | t.Fatal("Stmt not closed after Commit") |
| 560 | } |
| 561 | } |
| 562 | |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 563 | func TestTxStmt(t *testing.T) { |
| 564 | db := newTestDB(t, "") |
| 565 | defer closeDB(t, db) |
| 566 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 567 | stmt, err := db.Prepare("INSERT|t1|name=?,age=?") |
| 568 | if err != nil { |
| 569 | t.Fatalf("Stmt, err = %v, %v", stmt, err) |
| 570 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 571 | defer stmt.Close() |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 572 | tx, err := db.Begin() |
| 573 | if err != nil { |
| 574 | t.Fatalf("Begin = %v", err) |
| 575 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 576 | txs := tx.Stmt(stmt) |
| 577 | defer txs.Close() |
| 578 | _, err = txs.Exec("Bobby", 7) |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 579 | if err != nil { |
| 580 | t.Fatalf("Exec = %v", err) |
| 581 | } |
| 582 | err = tx.Commit() |
| 583 | if err != nil { |
| 584 | t.Fatalf("Commit = %v", err) |
| 585 | } |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 586 | // Commit() should have closed the statement |
| 587 | if !txs.closed { |
| 588 | t.Fatal("Stmt not closed after Commit") |
| 589 | } |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 590 | } |
Brad Fitzpatrick | 06a9bc6 | 2011-12-12 13:56:56 -0800 | [diff] [blame] | 591 | |
Brad Fitzpatrick | 2ae7737 | 2015-07-10 17:17:11 -0600 | [diff] [blame] | 592 | // Issue: https://golang.org/issue/2784 |
Robert Hencke | f999e14 | 2014-04-29 12:44:40 -0400 | [diff] [blame] | 593 | // This test didn't fail before because we got lucky with the fakedb driver. |
Blake Mizerany | bcb976c | 2012-01-25 17:49:30 -0800 | [diff] [blame] | 594 | // It was failing, and now not, in github.com/bradfitz/go-sql-test |
| 595 | func TestTxQuery(t *testing.T) { |
| 596 | db := newTestDB(t, "") |
| 597 | defer closeDB(t, db) |
| 598 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 599 | exec(t, db, "INSERT|t1|name=Alice") |
| 600 | |
| 601 | tx, err := db.Begin() |
| 602 | if err != nil { |
| 603 | t.Fatal(err) |
| 604 | } |
| 605 | defer tx.Rollback() |
| 606 | |
| 607 | r, err := tx.Query("SELECT|t1|name|") |
| 608 | if err != nil { |
| 609 | t.Fatal(err) |
| 610 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 611 | defer r.Close() |
Blake Mizerany | bcb976c | 2012-01-25 17:49:30 -0800 | [diff] [blame] | 612 | |
| 613 | if !r.Next() { |
| 614 | if r.Err() != nil { |
| 615 | t.Fatal(r.Err()) |
| 616 | } |
| 617 | t.Fatal("expected one row") |
| 618 | } |
| 619 | |
| 620 | var x string |
| 621 | err = r.Scan(&x) |
| 622 | if err != nil { |
| 623 | t.Fatal(err) |
| 624 | } |
| 625 | } |
| 626 | |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 627 | func TestTxQueryInvalid(t *testing.T) { |
| 628 | db := newTestDB(t, "") |
| 629 | defer closeDB(t, db) |
| 630 | |
| 631 | tx, err := db.Begin() |
| 632 | if err != nil { |
| 633 | t.Fatal(err) |
| 634 | } |
| 635 | defer tx.Rollback() |
| 636 | |
| 637 | _, err = tx.Query("SELECT|t1|name|") |
| 638 | if err == nil { |
| 639 | t.Fatal("Error expected") |
| 640 | } |
| 641 | } |
| 642 | |
James David Chalfant | 19e2f26 | 2012-12-14 09:00:33 -0800 | [diff] [blame] | 643 | // Tests fix for issue 4433, that retries in Begin happen when |
| 644 | // conn.Begin() returns ErrBadConn |
| 645 | func TestTxErrBadConn(t *testing.T) { |
| 646 | db, err := Open("test", fakeDBName+";badConn") |
| 647 | if err != nil { |
| 648 | t.Fatalf("Open: %v", err) |
| 649 | } |
| 650 | if _, err := db.Exec("WIPE"); err != nil { |
| 651 | t.Fatalf("exec wipe: %v", err) |
| 652 | } |
| 653 | defer closeDB(t, db) |
| 654 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 655 | stmt, err := db.Prepare("INSERT|t1|name=?,age=?") |
| 656 | if err != nil { |
| 657 | t.Fatalf("Stmt, err = %v, %v", stmt, err) |
| 658 | } |
| 659 | defer stmt.Close() |
| 660 | tx, err := db.Begin() |
| 661 | if err != nil { |
| 662 | t.Fatalf("Begin = %v", err) |
| 663 | } |
| 664 | txs := tx.Stmt(stmt) |
| 665 | defer txs.Close() |
| 666 | _, err = txs.Exec("Bobby", 7) |
| 667 | if err != nil { |
| 668 | t.Fatalf("Exec = %v", err) |
| 669 | } |
| 670 | err = tx.Commit() |
| 671 | if err != nil { |
| 672 | t.Fatalf("Commit = %v", err) |
| 673 | } |
| 674 | } |
| 675 | |
Brad Fitzpatrick | 06a9bc6 | 2011-12-12 13:56:56 -0800 | [diff] [blame] | 676 | // Tests fix for issue 2542, that we release a lock when querying on |
| 677 | // a closed connection. |
| 678 | func TestIssue2542Deadlock(t *testing.T) { |
| 679 | db := newTestDB(t, "people") |
| 680 | closeDB(t, db) |
| 681 | for i := 0; i < 2; i++ { |
| 682 | _, err := db.Query("SELECT|people|age,name|") |
| 683 | if err == nil { |
| 684 | t.Fatalf("expected error") |
| 685 | } |
| 686 | } |
| 687 | } |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 688 | |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 689 | // From golang.org/issue/3865 |
Brad Fitzpatrick | 8f2430a | 2013-02-13 12:00:03 -0800 | [diff] [blame] | 690 | func TestCloseStmtBeforeRows(t *testing.T) { |
Brad Fitzpatrick | 8f2430a | 2013-02-13 12:00:03 -0800 | [diff] [blame] | 691 | db := newTestDB(t, "people") |
| 692 | defer closeDB(t, db) |
| 693 | |
| 694 | s, err := db.Prepare("SELECT|people|name|") |
| 695 | if err != nil { |
| 696 | t.Fatal(err) |
| 697 | } |
| 698 | |
| 699 | r, err := s.Query() |
| 700 | if err != nil { |
| 701 | s.Close() |
| 702 | t.Fatal(err) |
| 703 | } |
| 704 | |
| 705 | err = s.Close() |
| 706 | if err != nil { |
| 707 | t.Fatal(err) |
| 708 | } |
| 709 | |
| 710 | r.Close() |
| 711 | } |
| 712 | |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 713 | // Tests fix for issue 2788, that we bind nil to a []byte if the |
| 714 | // value in the column is sql null |
| 715 | func TestNullByteSlice(t *testing.T) { |
| 716 | db := newTestDB(t, "") |
| 717 | defer closeDB(t, db) |
| 718 | exec(t, db, "CREATE|t|id=int32,name=nullstring") |
| 719 | exec(t, db, "INSERT|t|id=10,name=?", nil) |
| 720 | |
| 721 | var name []byte |
| 722 | |
| 723 | err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) |
| 724 | if err != nil { |
| 725 | t.Fatal(err) |
| 726 | } |
| 727 | if name != nil { |
| 728 | t.Fatalf("name []byte should be nil for null column value, got: %#v", name) |
| 729 | } |
| 730 | |
| 731 | exec(t, db, "INSERT|t|id=11,name=?", "bob") |
| 732 | err = db.QueryRow("SELECT|t|name|id=?", 11).Scan(&name) |
| 733 | if err != nil { |
| 734 | t.Fatal(err) |
| 735 | } |
| 736 | if string(name) != "bob" { |
| 737 | t.Fatalf("name []byte should be bob, got: %q", string(name)) |
| 738 | } |
| 739 | } |
| 740 | |
Brad Fitzpatrick | 29df937 | 2012-02-09 15:01:29 +1100 | [diff] [blame] | 741 | func TestPointerParamsAndScans(t *testing.T) { |
| 742 | db := newTestDB(t, "") |
| 743 | defer closeDB(t, db) |
| 744 | exec(t, db, "CREATE|t|id=int32,name=nullstring") |
| 745 | |
| 746 | bob := "bob" |
| 747 | var name *string |
| 748 | |
| 749 | name = &bob |
| 750 | exec(t, db, "INSERT|t|id=10,name=?", name) |
| 751 | name = nil |
| 752 | exec(t, db, "INSERT|t|id=20,name=?", name) |
| 753 | |
| 754 | err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) |
| 755 | if err != nil { |
| 756 | t.Fatalf("querying id 10: %v", err) |
| 757 | } |
| 758 | if name == nil { |
| 759 | t.Errorf("id 10's name = nil; want bob") |
| 760 | } else if *name != "bob" { |
| 761 | t.Errorf("id 10's name = %q; want bob", *name) |
| 762 | } |
| 763 | |
| 764 | err = db.QueryRow("SELECT|t|name|id=?", 20).Scan(&name) |
| 765 | if err != nil { |
| 766 | t.Fatalf("querying id 20: %v", err) |
| 767 | } |
| 768 | if name != nil { |
| 769 | t.Errorf("id 20 = %q; want nil", *name) |
| 770 | } |
| 771 | } |
| 772 | |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 773 | func TestQueryRowClosingStmt(t *testing.T) { |
| 774 | db := newTestDB(t, "people") |
| 775 | defer closeDB(t, db) |
| 776 | var name string |
| 777 | var age int |
| 778 | err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) |
| 779 | if err != nil { |
| 780 | t.Fatal(err) |
| 781 | } |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 782 | if len(db.freeConn) != 1 { |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 783 | t.Fatalf("expected 1 free conn") |
| 784 | } |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 785 | fakeConn := db.freeConn[0].ci.(*fakeConn) |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 786 | if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed { |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 787 | t.Errorf("statement close mismatch: made %d, closed %d", made, closed) |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 788 | } |
| 789 | } |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 790 | |
Marko Tiikkaja | 1f20ab1 | 2013-12-16 12:48:35 -0800 | [diff] [blame] | 791 | // Test issue 6651 |
| 792 | func TestIssue6651(t *testing.T) { |
| 793 | db := newTestDB(t, "people") |
| 794 | defer closeDB(t, db) |
| 795 | |
| 796 | var v string |
| 797 | |
| 798 | want := "error in rows.Next" |
| 799 | rowsCursorNextHook = func(dest []driver.Value) error { |
| 800 | return fmt.Errorf(want) |
| 801 | } |
| 802 | defer func() { rowsCursorNextHook = nil }() |
| 803 | err := db.QueryRow("SELECT|people|name|").Scan(&v) |
| 804 | if err == nil || err.Error() != want { |
| 805 | t.Errorf("error = %q; want %q", err, want) |
| 806 | } |
| 807 | rowsCursorNextHook = nil |
| 808 | |
| 809 | want = "error in rows.Close" |
| 810 | rowsCloseHook = func(rows *Rows, err *error) { |
| 811 | *err = fmt.Errorf(want) |
| 812 | } |
| 813 | defer func() { rowsCloseHook = nil }() |
| 814 | err = db.QueryRow("SELECT|people|name|").Scan(&v) |
| 815 | if err == nil || err.Error() != want { |
| 816 | t.Errorf("error = %q; want %q", err, want) |
| 817 | } |
| 818 | } |
| 819 | |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 820 | type nullTestRow struct { |
| 821 | nullParam interface{} |
| 822 | notNullParam interface{} |
| 823 | scanNullVal interface{} |
| 824 | } |
| 825 | |
| 826 | type nullTestSpec struct { |
| 827 | nullType string |
| 828 | notNullType string |
| 829 | rows [6]nullTestRow |
| 830 | } |
| 831 | |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 832 | func TestNullStringParam(t *testing.T) { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 833 | spec := nullTestSpec{"nullstring", "string", [6]nullTestRow{ |
Robert Griesemer | 56cae1c | 2012-03-08 10:48:51 -0800 | [diff] [blame] | 834 | {NullString{"aqua", true}, "", NullString{"aqua", true}}, |
| 835 | {NullString{"brown", false}, "", NullString{"", false}}, |
| 836 | {"chartreuse", "", NullString{"chartreuse", true}}, |
| 837 | {NullString{"darkred", true}, "", NullString{"darkred", true}}, |
| 838 | {NullString{"eel", false}, "", NullString{"", false}}, |
| 839 | {"foo", NullString{"black", false}, nil}, |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 840 | }} |
| 841 | nullTestRun(t, spec) |
| 842 | } |
| 843 | |
| 844 | func TestNullInt64Param(t *testing.T) { |
| 845 | spec := nullTestSpec{"nullint64", "int64", [6]nullTestRow{ |
Robert Griesemer | 56cae1c | 2012-03-08 10:48:51 -0800 | [diff] [blame] | 846 | {NullInt64{31, true}, 1, NullInt64{31, true}}, |
| 847 | {NullInt64{-22, false}, 1, NullInt64{0, false}}, |
| 848 | {22, 1, NullInt64{22, true}}, |
| 849 | {NullInt64{33, true}, 1, NullInt64{33, true}}, |
| 850 | {NullInt64{222, false}, 1, NullInt64{0, false}}, |
| 851 | {0, NullInt64{31, false}, nil}, |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 852 | }} |
| 853 | nullTestRun(t, spec) |
| 854 | } |
| 855 | |
| 856 | func TestNullFloat64Param(t *testing.T) { |
| 857 | spec := nullTestSpec{"nullfloat64", "float64", [6]nullTestRow{ |
Robert Griesemer | 56cae1c | 2012-03-08 10:48:51 -0800 | [diff] [blame] | 858 | {NullFloat64{31.2, true}, 1, NullFloat64{31.2, true}}, |
| 859 | {NullFloat64{13.1, false}, 1, NullFloat64{0, false}}, |
| 860 | {-22.9, 1, NullFloat64{-22.9, true}}, |
| 861 | {NullFloat64{33.81, true}, 1, NullFloat64{33.81, true}}, |
| 862 | {NullFloat64{222, false}, 1, NullFloat64{0, false}}, |
| 863 | {10, NullFloat64{31.2, false}, nil}, |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 864 | }} |
| 865 | nullTestRun(t, spec) |
| 866 | } |
| 867 | |
| 868 | func TestNullBoolParam(t *testing.T) { |
| 869 | spec := nullTestSpec{"nullbool", "bool", [6]nullTestRow{ |
Robert Griesemer | 56cae1c | 2012-03-08 10:48:51 -0800 | [diff] [blame] | 870 | {NullBool{false, true}, true, NullBool{false, true}}, |
| 871 | {NullBool{true, false}, false, NullBool{false, false}}, |
| 872 | {true, true, NullBool{true, true}}, |
| 873 | {NullBool{true, true}, false, NullBool{true, true}}, |
| 874 | {NullBool{true, false}, true, NullBool{false, false}}, |
| 875 | {true, NullBool{true, false}, nil}, |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 876 | }} |
| 877 | nullTestRun(t, spec) |
| 878 | } |
| 879 | |
| 880 | func nullTestRun(t *testing.T, spec nullTestSpec) { |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 881 | db := newTestDB(t, "") |
| 882 | defer closeDB(t, db) |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 883 | exec(t, db, fmt.Sprintf("CREATE|t|id=int32,name=string,nullf=%s,notnullf=%s", spec.nullType, spec.notNullType)) |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 884 | |
| 885 | // Inserts with db.Exec: |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 886 | exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 1, "alice", spec.rows[0].nullParam, spec.rows[0].notNullParam) |
| 887 | exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 2, "bob", spec.rows[1].nullParam, spec.rows[1].notNullParam) |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 888 | |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 889 | // Inserts with a prepared statement: |
| 890 | stmt, err := db.Prepare("INSERT|t|id=?,name=?,nullf=?,notnullf=?") |
| 891 | if err != nil { |
| 892 | t.Fatalf("prepare: %v", err) |
| 893 | } |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 894 | defer stmt.Close() |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 895 | if _, err := stmt.Exec(3, "chris", spec.rows[2].nullParam, spec.rows[2].notNullParam); err != nil { |
| 896 | t.Errorf("exec insert chris: %v", err) |
| 897 | } |
| 898 | if _, err := stmt.Exec(4, "dave", spec.rows[3].nullParam, spec.rows[3].notNullParam); err != nil { |
| 899 | t.Errorf("exec insert dave: %v", err) |
| 900 | } |
| 901 | if _, err := stmt.Exec(5, "eleanor", spec.rows[4].nullParam, spec.rows[4].notNullParam); err != nil { |
| 902 | t.Errorf("exec insert eleanor: %v", err) |
| 903 | } |
| 904 | |
| 905 | // Can't put null val into non-null col |
| 906 | if _, err := stmt.Exec(6, "bob", spec.rows[5].nullParam, spec.rows[5].notNullParam); err == nil { |
| 907 | t.Errorf("expected error inserting nil val with prepared statement Exec") |
| 908 | } |
| 909 | |
| 910 | _, err = db.Exec("INSERT|t|id=?,name=?,nullf=?", 999, nil, nil) |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 911 | if err == nil { |
| 912 | // TODO: this test fails, but it's just because |
| 913 | // fakeConn implements the optional Execer interface, |
| 914 | // so arguably this is the correct behavior. But |
| 915 | // maybe I should flesh out the fakeConn.Exec |
| 916 | // implementation so this properly fails. |
| 917 | // t.Errorf("expected error inserting nil name with Exec") |
| 918 | } |
| 919 | |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 920 | paramtype := reflect.TypeOf(spec.rows[0].nullParam) |
| 921 | bindVal := reflect.New(paramtype).Interface() |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 922 | |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 923 | for i := 0; i < 5; i++ { |
| 924 | id := i + 1 |
| 925 | if err := db.QueryRow("SELECT|t|nullf|id=?", id).Scan(bindVal); err != nil { |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 926 | t.Errorf("id=%d Scan: %v", id, err) |
| 927 | } |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 928 | bindValDeref := reflect.ValueOf(bindVal).Elem().Interface() |
| 929 | if !reflect.DeepEqual(bindValDeref, spec.rows[i].scanNullVal) { |
| 930 | t.Errorf("id=%d got %#v, want %#v", id, bindValDeref, spec.rows[i].scanNullVal) |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 931 | } |
| 932 | } |
| 933 | } |
Brad Fitzpatrick | bca3f5f | 2013-02-21 10:43:00 -0800 | [diff] [blame] | 934 | |
| 935 | // golang.org/issue/4859 |
| 936 | func TestQueryRowNilScanDest(t *testing.T) { |
| 937 | db := newTestDB(t, "people") |
| 938 | defer closeDB(t, db) |
| 939 | var name *string // nil pointer |
| 940 | err := db.QueryRow("SELECT|people|name|").Scan(name) |
| 941 | want := "sql: Scan error on column index 0: destination pointer is nil" |
| 942 | if err == nil || err.Error() != want { |
| 943 | t.Errorf("error = %q; want %q", err.Error(), want) |
| 944 | } |
| 945 | } |
Brad Fitzpatrick | 3cdf8ba | 2013-03-08 10:04:17 -0800 | [diff] [blame] | 946 | |
| 947 | func TestIssue4902(t *testing.T) { |
| 948 | db := newTestDB(t, "people") |
| 949 | defer closeDB(t, db) |
| 950 | |
| 951 | driver := db.driver.(*fakeDriver) |
| 952 | opens0 := driver.openCount |
| 953 | |
| 954 | var stmt *Stmt |
| 955 | var err error |
| 956 | for i := 0; i < 10; i++ { |
| 957 | stmt, err = db.Prepare("SELECT|people|name|") |
| 958 | if err != nil { |
| 959 | t.Fatal(err) |
| 960 | } |
| 961 | err = stmt.Close() |
| 962 | if err != nil { |
| 963 | t.Fatal(err) |
| 964 | } |
| 965 | } |
| 966 | |
| 967 | opens := driver.openCount - opens0 |
| 968 | if opens > 1 { |
| 969 | t.Errorf("opens = %d; want <= 1", opens) |
| 970 | t.Logf("db = %#v", db) |
| 971 | t.Logf("driver = %#v", driver) |
| 972 | t.Logf("stmt = %#v", stmt) |
| 973 | } |
| 974 | } |
Brad Fitzpatrick | a7a803c | 2013-03-18 11:39:00 -0700 | [diff] [blame] | 975 | |
| 976 | // Issue 3857 |
| 977 | // This used to deadlock. |
| 978 | func TestSimultaneousQueries(t *testing.T) { |
| 979 | db := newTestDB(t, "people") |
| 980 | defer closeDB(t, db) |
| 981 | |
| 982 | tx, err := db.Begin() |
| 983 | if err != nil { |
| 984 | t.Fatal(err) |
| 985 | } |
| 986 | defer tx.Rollback() |
| 987 | |
| 988 | r1, err := tx.Query("SELECT|people|name|") |
| 989 | if err != nil { |
| 990 | t.Fatal(err) |
| 991 | } |
| 992 | defer r1.Close() |
| 993 | |
| 994 | r2, err := tx.Query("SELECT|people|name|") |
| 995 | if err != nil { |
| 996 | t.Fatal(err) |
| 997 | } |
| 998 | defer r2.Close() |
| 999 | } |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 1000 | |
| 1001 | func TestMaxIdleConns(t *testing.T) { |
| 1002 | db := newTestDB(t, "people") |
| 1003 | defer closeDB(t, db) |
| 1004 | |
| 1005 | tx, err := db.Begin() |
| 1006 | if err != nil { |
| 1007 | t.Fatal(err) |
| 1008 | } |
| 1009 | tx.Commit() |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 1010 | if got := len(db.freeConn); got != 1 { |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 1011 | t.Errorf("freeConns = %d; want 1", got) |
| 1012 | } |
| 1013 | |
| 1014 | db.SetMaxIdleConns(0) |
| 1015 | |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 1016 | if got := len(db.freeConn); got != 0 { |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 1017 | t.Errorf("freeConns after set to zero = %d; want 0", got) |
| 1018 | } |
| 1019 | |
| 1020 | tx, err = db.Begin() |
| 1021 | if err != nil { |
| 1022 | t.Fatal(err) |
| 1023 | } |
| 1024 | tx.Commit() |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 1025 | if got := len(db.freeConn); got != 0 { |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 1026 | t.Errorf("freeConns = %d; want 0", got) |
| 1027 | } |
| 1028 | } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1029 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1030 | func TestMaxOpenConns(t *testing.T) { |
| 1031 | if testing.Short() { |
| 1032 | t.Skip("skipping in short mode") |
| 1033 | } |
| 1034 | defer setHookpostCloseConn(nil) |
| 1035 | setHookpostCloseConn(func(_ *fakeConn, err error) { |
| 1036 | if err != nil { |
| 1037 | t.Errorf("Error closing fakeConn: %v", err) |
| 1038 | } |
| 1039 | }) |
| 1040 | |
| 1041 | db := newTestDB(t, "magicquery") |
| 1042 | defer closeDB(t, db) |
| 1043 | |
| 1044 | driver := db.driver.(*fakeDriver) |
| 1045 | |
| 1046 | // Force the number of open connections to 0 so we can get an accurate |
| 1047 | // count for the test |
INADA Naoki | 0c516c1 | 2015-03-03 21:27:07 +0900 | [diff] [blame] | 1048 | db.clearAllConns(t) |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1049 | |
| 1050 | driver.mu.Lock() |
| 1051 | opens0 := driver.openCount |
| 1052 | closes0 := driver.closeCount |
| 1053 | driver.mu.Unlock() |
| 1054 | |
| 1055 | db.SetMaxIdleConns(10) |
| 1056 | db.SetMaxOpenConns(10) |
| 1057 | |
| 1058 | stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") |
| 1059 | if err != nil { |
| 1060 | t.Fatal(err) |
| 1061 | } |
| 1062 | |
| 1063 | // Start 50 parallel slow queries. |
| 1064 | const ( |
| 1065 | nquery = 50 |
| 1066 | sleepMillis = 25 |
| 1067 | nbatch = 2 |
| 1068 | ) |
| 1069 | var wg sync.WaitGroup |
| 1070 | for batch := 0; batch < nbatch; batch++ { |
| 1071 | for i := 0; i < nquery; i++ { |
| 1072 | wg.Add(1) |
| 1073 | go func() { |
| 1074 | defer wg.Done() |
| 1075 | var op string |
| 1076 | if err := stmt.QueryRow("sleep", sleepMillis).Scan(&op); err != nil && err != ErrNoRows { |
| 1077 | t.Error(err) |
| 1078 | } |
| 1079 | }() |
| 1080 | } |
| 1081 | // Sleep for twice the expected length of time for the |
| 1082 | // batch of 50 queries above to finish before starting |
| 1083 | // the next round. |
| 1084 | time.Sleep(2 * sleepMillis * time.Millisecond) |
| 1085 | } |
| 1086 | wg.Wait() |
| 1087 | |
| 1088 | if g, w := db.numFreeConns(), 10; g != w { |
| 1089 | t.Errorf("free conns = %d; want %d", g, w) |
| 1090 | } |
| 1091 | |
| 1092 | if n := db.numDepsPollUntil(20, time.Second); n > 20 { |
| 1093 | t.Errorf("number of dependencies = %d; expected <= 20", n) |
| 1094 | db.dumpDeps(t) |
| 1095 | } |
| 1096 | |
| 1097 | driver.mu.Lock() |
| 1098 | opens := driver.openCount - opens0 |
| 1099 | closes := driver.closeCount - closes0 |
| 1100 | driver.mu.Unlock() |
| 1101 | |
| 1102 | if opens > 10 { |
| 1103 | t.Logf("open calls = %d", opens) |
| 1104 | t.Logf("close calls = %d", closes) |
| 1105 | t.Errorf("db connections opened = %d; want <= 10", opens) |
| 1106 | db.dumpDeps(t) |
| 1107 | } |
| 1108 | |
| 1109 | if err := stmt.Close(); err != nil { |
| 1110 | t.Fatal(err) |
| 1111 | } |
| 1112 | |
| 1113 | if g, w := db.numFreeConns(), 10; g != w { |
| 1114 | t.Errorf("free conns = %d; want %d", g, w) |
| 1115 | } |
| 1116 | |
| 1117 | if n := db.numDepsPollUntil(10, time.Second); n > 10 { |
| 1118 | t.Errorf("number of dependencies = %d; expected <= 10", n) |
| 1119 | db.dumpDeps(t) |
| 1120 | } |
| 1121 | |
| 1122 | db.SetMaxOpenConns(5) |
| 1123 | |
| 1124 | if g, w := db.numFreeConns(), 5; g != w { |
| 1125 | t.Errorf("free conns = %d; want %d", g, w) |
| 1126 | } |
| 1127 | |
| 1128 | if n := db.numDepsPollUntil(5, time.Second); n > 5 { |
| 1129 | t.Errorf("number of dependencies = %d; expected 0", n) |
| 1130 | db.dumpDeps(t) |
| 1131 | } |
| 1132 | |
| 1133 | db.SetMaxOpenConns(0) |
| 1134 | |
| 1135 | if g, w := db.numFreeConns(), 5; g != w { |
| 1136 | t.Errorf("free conns = %d; want %d", g, w) |
| 1137 | } |
| 1138 | |
| 1139 | if n := db.numDepsPollUntil(5, time.Second); n > 5 { |
| 1140 | t.Errorf("number of dependencies = %d; expected 0", n) |
| 1141 | db.dumpDeps(t) |
| 1142 | } |
| 1143 | |
INADA Naoki | 0c516c1 | 2015-03-03 21:27:07 +0900 | [diff] [blame] | 1144 | db.clearAllConns(t) |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1145 | } |
| 1146 | |
Jiong Du | cce127a | 2014-12-30 16:12:50 +0800 | [diff] [blame] | 1147 | // Issue 9453: tests that SetMaxOpenConns can be lowered at runtime |
| 1148 | // and affects the subsequent release of connections. |
| 1149 | func TestMaxOpenConnsOnBusy(t *testing.T) { |
| 1150 | defer setHookpostCloseConn(nil) |
| 1151 | setHookpostCloseConn(func(_ *fakeConn, err error) { |
| 1152 | if err != nil { |
| 1153 | t.Errorf("Error closing fakeConn: %v", err) |
| 1154 | } |
| 1155 | }) |
| 1156 | |
| 1157 | db := newTestDB(t, "magicquery") |
| 1158 | defer closeDB(t, db) |
| 1159 | |
| 1160 | db.SetMaxOpenConns(3) |
| 1161 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1162 | conn0, err := db.conn(cachedOrNewConn) |
Jiong Du | cce127a | 2014-12-30 16:12:50 +0800 | [diff] [blame] | 1163 | if err != nil { |
| 1164 | t.Fatalf("db open conn fail: %v", err) |
| 1165 | } |
| 1166 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1167 | conn1, err := db.conn(cachedOrNewConn) |
Jiong Du | cce127a | 2014-12-30 16:12:50 +0800 | [diff] [blame] | 1168 | if err != nil { |
| 1169 | t.Fatalf("db open conn fail: %v", err) |
| 1170 | } |
| 1171 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1172 | conn2, err := db.conn(cachedOrNewConn) |
Jiong Du | cce127a | 2014-12-30 16:12:50 +0800 | [diff] [blame] | 1173 | if err != nil { |
| 1174 | t.Fatalf("db open conn fail: %v", err) |
| 1175 | } |
| 1176 | |
| 1177 | if g, w := db.numOpen, 3; g != w { |
| 1178 | t.Errorf("free conns = %d; want %d", g, w) |
| 1179 | } |
| 1180 | |
| 1181 | db.SetMaxOpenConns(2) |
| 1182 | if g, w := db.numOpen, 3; g != w { |
| 1183 | t.Errorf("free conns = %d; want %d", g, w) |
| 1184 | } |
| 1185 | |
| 1186 | conn0.releaseConn(nil) |
| 1187 | conn1.releaseConn(nil) |
| 1188 | if g, w := db.numOpen, 2; g != w { |
| 1189 | t.Errorf("free conns = %d; want %d", g, w) |
| 1190 | } |
| 1191 | |
| 1192 | conn2.releaseConn(nil) |
| 1193 | if g, w := db.numOpen, 2; g != w { |
| 1194 | t.Errorf("free conns = %d; want %d", g, w) |
| 1195 | } |
| 1196 | } |
| 1197 | |
Chris Hines | 6de4009 | 2015-09-14 03:44:56 -0400 | [diff] [blame] | 1198 | // Issue 10886: tests that all connection attempts return when more than |
| 1199 | // DB.maxOpen connections are in flight and the first DB.maxOpen fail. |
| 1200 | func TestPendingConnsAfterErr(t *testing.T) { |
| 1201 | const ( |
| 1202 | maxOpen = 2 |
| 1203 | tryOpen = maxOpen*2 + 2 |
| 1204 | ) |
| 1205 | |
| 1206 | db := newTestDB(t, "people") |
| 1207 | defer closeDB(t, db) |
| 1208 | defer func() { |
| 1209 | for k, v := range db.lastPut { |
| 1210 | t.Logf("%p: %v", k, v) |
| 1211 | } |
| 1212 | }() |
| 1213 | |
| 1214 | db.SetMaxOpenConns(maxOpen) |
| 1215 | db.SetMaxIdleConns(0) |
| 1216 | |
| 1217 | errOffline := errors.New("db offline") |
| 1218 | defer func() { setHookOpenErr(nil) }() |
| 1219 | |
| 1220 | errs := make(chan error, tryOpen) |
| 1221 | |
| 1222 | unblock := make(chan struct{}) |
| 1223 | setHookOpenErr(func() error { |
| 1224 | <-unblock // block until all connections are in flight |
| 1225 | return errOffline |
| 1226 | }) |
| 1227 | |
| 1228 | var opening sync.WaitGroup |
| 1229 | opening.Add(tryOpen) |
| 1230 | for i := 0; i < tryOpen; i++ { |
| 1231 | go func() { |
| 1232 | opening.Done() // signal one connection is in flight |
| 1233 | _, err := db.Exec("INSERT|people|name=Julia,age=19") |
| 1234 | errs <- err |
| 1235 | }() |
| 1236 | } |
| 1237 | |
| 1238 | opening.Wait() // wait for all workers to begin running |
| 1239 | time.Sleep(10 * time.Millisecond) // make extra sure all workers are blocked |
| 1240 | close(unblock) // let all workers proceed |
| 1241 | |
| 1242 | const timeout = 100 * time.Millisecond |
| 1243 | to := time.NewTimer(timeout) |
| 1244 | defer to.Stop() |
| 1245 | |
| 1246 | // check that all connections fail without deadlock |
| 1247 | for i := 0; i < tryOpen; i++ { |
| 1248 | select { |
| 1249 | case err := <-errs: |
| 1250 | if got, want := err, errOffline; got != want { |
| 1251 | t.Errorf("unexpected err: got %v, want %v", got, want) |
| 1252 | } |
| 1253 | case <-to.C: |
| 1254 | t.Fatalf("orphaned connection request(s), still waiting after %v", timeout) |
| 1255 | } |
| 1256 | } |
| 1257 | } |
| 1258 | |
Marko Tiikkaja | 0d12e24 | 2013-12-26 11:27:18 -0800 | [diff] [blame] | 1259 | func TestSingleOpenConn(t *testing.T) { |
| 1260 | db := newTestDB(t, "people") |
| 1261 | defer closeDB(t, db) |
| 1262 | |
| 1263 | db.SetMaxOpenConns(1) |
| 1264 | |
| 1265 | rows, err := db.Query("SELECT|people|name|") |
| 1266 | if err != nil { |
| 1267 | t.Fatal(err) |
| 1268 | } |
| 1269 | if err = rows.Close(); err != nil { |
| 1270 | t.Fatal(err) |
| 1271 | } |
| 1272 | // shouldn't deadlock |
| 1273 | rows, err = db.Query("SELECT|people|name|") |
| 1274 | if err != nil { |
| 1275 | t.Fatal(err) |
| 1276 | } |
| 1277 | if err = rows.Close(); err != nil { |
| 1278 | t.Fatal(err) |
| 1279 | } |
| 1280 | } |
| 1281 | |
Andrei Korzhevskii | 297c1d2 | 2015-03-23 18:23:53 +0300 | [diff] [blame] | 1282 | func TestStats(t *testing.T) { |
| 1283 | db := newTestDB(t, "people") |
| 1284 | stats := db.Stats() |
| 1285 | if got := stats.OpenConnections; got != 1 { |
| 1286 | t.Errorf("stats.OpenConnections = %d; want 1", got) |
| 1287 | } |
| 1288 | |
| 1289 | tx, err := db.Begin() |
| 1290 | if err != nil { |
| 1291 | t.Fatal(err) |
| 1292 | } |
| 1293 | tx.Commit() |
| 1294 | |
| 1295 | closeDB(t, db) |
| 1296 | stats = db.Stats() |
| 1297 | if got := stats.OpenConnections; got != 0 { |
| 1298 | t.Errorf("stats.OpenConnections = %d; want 0", got) |
| 1299 | } |
| 1300 | } |
| 1301 | |
INADA Naoki | 0c516c1 | 2015-03-03 21:27:07 +0900 | [diff] [blame] | 1302 | func TestConnMaxLifetime(t *testing.T) { |
| 1303 | t0 := time.Unix(1000000, 0) |
| 1304 | offset := time.Duration(0) |
| 1305 | |
| 1306 | nowFunc = func() time.Time { return t0.Add(offset) } |
| 1307 | defer func() { nowFunc = time.Now }() |
| 1308 | |
| 1309 | db := newTestDB(t, "magicquery") |
| 1310 | defer closeDB(t, db) |
| 1311 | |
| 1312 | driver := db.driver.(*fakeDriver) |
| 1313 | |
| 1314 | // Force the number of open connections to 0 so we can get an accurate |
| 1315 | // count for the test |
| 1316 | db.clearAllConns(t) |
| 1317 | |
| 1318 | driver.mu.Lock() |
| 1319 | opens0 := driver.openCount |
| 1320 | closes0 := driver.closeCount |
| 1321 | driver.mu.Unlock() |
| 1322 | |
| 1323 | db.SetMaxIdleConns(10) |
| 1324 | db.SetMaxOpenConns(10) |
| 1325 | |
| 1326 | tx, err := db.Begin() |
| 1327 | if err != nil { |
| 1328 | t.Fatal(err) |
| 1329 | } |
| 1330 | |
| 1331 | offset = time.Second |
| 1332 | tx2, err := db.Begin() |
| 1333 | if err != nil { |
| 1334 | t.Fatal(err) |
| 1335 | } |
| 1336 | |
| 1337 | tx.Commit() |
| 1338 | tx2.Commit() |
| 1339 | |
| 1340 | driver.mu.Lock() |
| 1341 | opens := driver.openCount - opens0 |
| 1342 | closes := driver.closeCount - closes0 |
| 1343 | driver.mu.Unlock() |
| 1344 | |
| 1345 | if opens != 2 { |
| 1346 | t.Errorf("opens = %d; want 2", opens) |
| 1347 | } |
| 1348 | if closes != 0 { |
| 1349 | t.Errorf("closes = %d; want 0", closes) |
| 1350 | } |
| 1351 | if g, w := db.numFreeConns(), 2; g != w { |
| 1352 | t.Errorf("free conns = %d; want %d", g, w) |
| 1353 | } |
| 1354 | |
| 1355 | // Expire first conn |
| 1356 | offset = time.Second * 11 |
| 1357 | db.SetConnMaxLifetime(time.Second * 10) |
| 1358 | if err != nil { |
| 1359 | t.Fatal(err) |
| 1360 | } |
| 1361 | |
| 1362 | tx, err = db.Begin() |
| 1363 | if err != nil { |
| 1364 | t.Fatal(err) |
| 1365 | } |
| 1366 | tx2, err = db.Begin() |
| 1367 | if err != nil { |
| 1368 | t.Fatal(err) |
| 1369 | } |
| 1370 | tx.Commit() |
| 1371 | tx2.Commit() |
| 1372 | |
| 1373 | driver.mu.Lock() |
| 1374 | opens = driver.openCount - opens0 |
| 1375 | closes = driver.closeCount - closes0 |
| 1376 | driver.mu.Unlock() |
| 1377 | |
| 1378 | if opens != 3 { |
| 1379 | t.Errorf("opens = %d; want 3", opens) |
| 1380 | } |
| 1381 | if closes != 1 { |
| 1382 | t.Errorf("closes = %d; want 1", closes) |
| 1383 | } |
| 1384 | } |
| 1385 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1386 | // golang.org/issue/5323 |
| 1387 | func TestStmtCloseDeps(t *testing.T) { |
| 1388 | if testing.Short() { |
| 1389 | t.Skip("skipping in short mode") |
| 1390 | } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1391 | defer setHookpostCloseConn(nil) |
| 1392 | setHookpostCloseConn(func(_ *fakeConn, err error) { |
| 1393 | if err != nil { |
| 1394 | t.Errorf("Error closing fakeConn: %v", err) |
| 1395 | } |
| 1396 | }) |
| 1397 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1398 | db := newTestDB(t, "magicquery") |
| 1399 | defer closeDB(t, db) |
| 1400 | |
| 1401 | driver := db.driver.(*fakeDriver) |
| 1402 | |
| 1403 | driver.mu.Lock() |
| 1404 | opens0 := driver.openCount |
| 1405 | closes0 := driver.closeCount |
| 1406 | driver.mu.Unlock() |
| 1407 | openDelta0 := opens0 - closes0 |
| 1408 | |
| 1409 | stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") |
| 1410 | if err != nil { |
| 1411 | t.Fatal(err) |
| 1412 | } |
| 1413 | |
| 1414 | // Start 50 parallel slow queries. |
| 1415 | const ( |
| 1416 | nquery = 50 |
| 1417 | sleepMillis = 25 |
| 1418 | nbatch = 2 |
| 1419 | ) |
| 1420 | var wg sync.WaitGroup |
| 1421 | for batch := 0; batch < nbatch; batch++ { |
| 1422 | for i := 0; i < nquery; i++ { |
| 1423 | wg.Add(1) |
| 1424 | go func() { |
| 1425 | defer wg.Done() |
| 1426 | var op string |
| 1427 | if err := stmt.QueryRow("sleep", sleepMillis).Scan(&op); err != nil && err != ErrNoRows { |
| 1428 | t.Error(err) |
| 1429 | } |
| 1430 | }() |
| 1431 | } |
| 1432 | // Sleep for twice the expected length of time for the |
| 1433 | // batch of 50 queries above to finish before starting |
| 1434 | // the next round. |
| 1435 | time.Sleep(2 * sleepMillis * time.Millisecond) |
| 1436 | } |
| 1437 | wg.Wait() |
| 1438 | |
| 1439 | if g, w := db.numFreeConns(), 2; g != w { |
| 1440 | t.Errorf("free conns = %d; want %d", g, w) |
| 1441 | } |
| 1442 | |
| 1443 | if n := db.numDepsPollUntil(4, time.Second); n > 4 { |
| 1444 | t.Errorf("number of dependencies = %d; expected <= 4", n) |
| 1445 | db.dumpDeps(t) |
| 1446 | } |
| 1447 | |
| 1448 | driver.mu.Lock() |
| 1449 | opens := driver.openCount - opens0 |
| 1450 | closes := driver.closeCount - closes0 |
Brad Fitzpatrick | 9456adb | 2013-08-29 17:26:00 -0700 | [diff] [blame] | 1451 | openDelta := (driver.openCount - driver.closeCount) - openDelta0 |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1452 | driver.mu.Unlock() |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1453 | |
| 1454 | if openDelta > 2 { |
| 1455 | t.Logf("open calls = %d", opens) |
| 1456 | t.Logf("close calls = %d", closes) |
| 1457 | t.Logf("open delta = %d", openDelta) |
| 1458 | t.Errorf("db connections opened = %d; want <= 2", openDelta) |
| 1459 | db.dumpDeps(t) |
| 1460 | } |
| 1461 | |
| 1462 | if len(stmt.css) > nquery { |
| 1463 | t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery) |
| 1464 | } |
| 1465 | |
| 1466 | if err := stmt.Close(); err != nil { |
| 1467 | t.Fatal(err) |
| 1468 | } |
| 1469 | |
| 1470 | if g, w := db.numFreeConns(), 2; g != w { |
| 1471 | t.Errorf("free conns = %d; want %d", g, w) |
| 1472 | } |
| 1473 | |
| 1474 | if n := db.numDepsPollUntil(2, time.Second); n > 2 { |
| 1475 | t.Errorf("number of dependencies = %d; expected <= 2", n) |
| 1476 | db.dumpDeps(t) |
| 1477 | } |
| 1478 | |
INADA Naoki | 0c516c1 | 2015-03-03 21:27:07 +0900 | [diff] [blame] | 1479 | db.clearAllConns(t) |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1480 | } |
| 1481 | |
| 1482 | // golang.org/issue/5046 |
| 1483 | func TestCloseConnBeforeStmts(t *testing.T) { |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1484 | db := newTestDB(t, "people") |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1485 | defer closeDB(t, db) |
| 1486 | |
| 1487 | defer setHookpostCloseConn(nil) |
| 1488 | setHookpostCloseConn(func(_ *fakeConn, err error) { |
| 1489 | if err != nil { |
| 1490 | t.Errorf("Error closing fakeConn: %v; from %s", err, stack()) |
| 1491 | db.dumpDeps(t) |
| 1492 | t.Errorf("DB = %#v", db) |
| 1493 | } |
| 1494 | }) |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1495 | |
| 1496 | stmt, err := db.Prepare("SELECT|people|name|") |
| 1497 | if err != nil { |
| 1498 | t.Fatal(err) |
| 1499 | } |
| 1500 | |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 1501 | if len(db.freeConn) != 1 { |
| 1502 | t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn)) |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1503 | } |
Alberto García Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 1504 | dc := db.freeConn[0] |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1505 | if dc.closed { |
| 1506 | t.Errorf("conn shouldn't be closed") |
| 1507 | } |
| 1508 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1509 | if n := len(dc.openStmt); n != 1 { |
| 1510 | t.Errorf("driverConn num openStmt = %d; want 1", n) |
| 1511 | } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1512 | err = db.Close() |
| 1513 | if err != nil { |
| 1514 | t.Errorf("db Close = %v", err) |
| 1515 | } |
| 1516 | if !dc.closed { |
| 1517 | t.Errorf("after db.Close, driverConn should be closed") |
| 1518 | } |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1519 | if n := len(dc.openStmt); n != 0 { |
| 1520 | t.Errorf("driverConn num openStmt = %d; want 0", n) |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 1521 | } |
| 1522 | |
| 1523 | err = stmt.Close() |
| 1524 | if err != nil { |
| 1525 | t.Errorf("Stmt close = %v", err) |
| 1526 | } |
| 1527 | |
| 1528 | if !dc.closed { |
| 1529 | t.Errorf("conn should be closed") |
| 1530 | } |
| 1531 | if dc.ci != nil { |
| 1532 | t.Errorf("after Stmt Close, driverConn's Conn interface should be nil") |
| 1533 | } |
| 1534 | } |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 1535 | |
Brad Fitzpatrick | 36d3bef | 2013-04-15 14:06:41 -0700 | [diff] [blame] | 1536 | // golang.org/issue/5283: don't release the Rows' connection in Close |
| 1537 | // before calling Stmt.Close. |
| 1538 | func TestRowsCloseOrder(t *testing.T) { |
| 1539 | db := newTestDB(t, "people") |
| 1540 | defer closeDB(t, db) |
| 1541 | |
| 1542 | db.SetMaxIdleConns(0) |
| 1543 | setStrictFakeConnClose(t) |
| 1544 | defer setStrictFakeConnClose(nil) |
| 1545 | |
| 1546 | rows, err := db.Query("SELECT|people|age,name|") |
| 1547 | if err != nil { |
| 1548 | t.Fatal(err) |
| 1549 | } |
| 1550 | err = rows.Close() |
| 1551 | if err != nil { |
| 1552 | t.Fatal(err) |
| 1553 | } |
| 1554 | } |
| 1555 | |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1556 | func TestRowsImplicitClose(t *testing.T) { |
| 1557 | db := newTestDB(t, "people") |
| 1558 | defer closeDB(t, db) |
| 1559 | |
| 1560 | rows, err := db.Query("SELECT|people|age,name|") |
| 1561 | if err != nil { |
| 1562 | t.Fatal(err) |
| 1563 | } |
| 1564 | |
| 1565 | want, fail := 2, errors.New("fail") |
| 1566 | r := rows.rowsi.(*rowsCursor) |
| 1567 | r.errPos, r.err = want, fail |
| 1568 | |
| 1569 | got := 0 |
| 1570 | for rows.Next() { |
| 1571 | got++ |
| 1572 | } |
| 1573 | if got != want { |
| 1574 | t.Errorf("got %d rows, want %d", got, want) |
| 1575 | } |
| 1576 | if err := rows.Err(); err != fail { |
| 1577 | t.Errorf("got error %v, want %v", err, fail) |
| 1578 | } |
| 1579 | if !r.closed { |
| 1580 | t.Errorf("r.closed is false, want true") |
| 1581 | } |
| 1582 | } |
| 1583 | |
Alex Brainman | a293065 | 2013-07-23 14:09:53 +1000 | [diff] [blame] | 1584 | func TestStmtCloseOrder(t *testing.T) { |
| 1585 | db := newTestDB(t, "people") |
| 1586 | defer closeDB(t, db) |
| 1587 | |
| 1588 | db.SetMaxIdleConns(0) |
| 1589 | setStrictFakeConnClose(t) |
| 1590 | defer setStrictFakeConnClose(nil) |
| 1591 | |
| 1592 | _, err := db.Query("SELECT|non_existent|name|") |
| 1593 | if err == nil { |
Martin Möhrmann | fdd0179 | 2016-02-24 11:55:20 +0100 | [diff] [blame] | 1594 | t.Fatal("Querying non-existent table should fail") |
Alex Brainman | a293065 | 2013-07-23 14:09:53 +1000 | [diff] [blame] | 1595 | } |
| 1596 | } |
| 1597 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1598 | // Test cases where there's more than maxBadConnRetries bad connections in the |
| 1599 | // pool (issue 8834) |
| 1600 | func TestManyErrBadConn(t *testing.T) { |
| 1601 | manyErrBadConnSetup := func() *DB { |
| 1602 | db := newTestDB(t, "people") |
| 1603 | |
| 1604 | nconn := maxBadConnRetries + 1 |
| 1605 | db.SetMaxIdleConns(nconn) |
| 1606 | db.SetMaxOpenConns(nconn) |
| 1607 | // open enough connections |
| 1608 | func() { |
| 1609 | for i := 0; i < nconn; i++ { |
| 1610 | rows, err := db.Query("SELECT|people|age,name|") |
| 1611 | if err != nil { |
| 1612 | t.Fatal(err) |
| 1613 | } |
| 1614 | defer rows.Close() |
| 1615 | } |
| 1616 | }() |
| 1617 | |
| 1618 | if db.numOpen != nconn { |
| 1619 | t.Fatalf("unexpected numOpen %d (was expecting %d)", db.numOpen, nconn) |
| 1620 | } else if len(db.freeConn) != nconn { |
| 1621 | t.Fatalf("unexpected len(db.freeConn) %d (was expecting %d)", len(db.freeConn), nconn) |
| 1622 | } |
| 1623 | for _, conn := range db.freeConn { |
| 1624 | conn.ci.(*fakeConn).stickyBad = true |
| 1625 | } |
| 1626 | return db |
| 1627 | } |
| 1628 | |
| 1629 | // Query |
| 1630 | db := manyErrBadConnSetup() |
| 1631 | defer closeDB(t, db) |
| 1632 | rows, err := db.Query("SELECT|people|age,name|") |
| 1633 | if err != nil { |
| 1634 | t.Fatal(err) |
| 1635 | } |
| 1636 | if err = rows.Close(); err != nil { |
| 1637 | t.Fatal(err) |
| 1638 | } |
| 1639 | |
| 1640 | // Exec |
| 1641 | db = manyErrBadConnSetup() |
| 1642 | defer closeDB(t, db) |
| 1643 | _, err = db.Exec("INSERT|people|name=Julia,age=19") |
| 1644 | if err != nil { |
| 1645 | t.Fatal(err) |
| 1646 | } |
| 1647 | |
| 1648 | // Begin |
| 1649 | db = manyErrBadConnSetup() |
| 1650 | defer closeDB(t, db) |
| 1651 | tx, err := db.Begin() |
| 1652 | if err != nil { |
| 1653 | t.Fatal(err) |
| 1654 | } |
| 1655 | if err = tx.Rollback(); err != nil { |
| 1656 | t.Fatal(err) |
| 1657 | } |
| 1658 | |
| 1659 | // Prepare |
| 1660 | db = manyErrBadConnSetup() |
| 1661 | defer closeDB(t, db) |
| 1662 | stmt, err := db.Prepare("SELECT|people|age,name|") |
| 1663 | if err != nil { |
| 1664 | t.Fatal(err) |
| 1665 | } |
| 1666 | if err = stmt.Close(); err != nil { |
| 1667 | t.Fatal(err) |
| 1668 | } |
| 1669 | } |
| 1670 | |
Alex Brainman | 5dbe071 | 2015-06-05 17:02:09 +1000 | [diff] [blame] | 1671 | // golang.org/issue/5718 |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1672 | func TestErrBadConnReconnect(t *testing.T) { |
| 1673 | db := newTestDB(t, "foo") |
| 1674 | defer closeDB(t, db) |
| 1675 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 1676 | |
| 1677 | simulateBadConn := func(name string, hook *func() bool, op func() error) { |
| 1678 | broken, retried := false, false |
| 1679 | numOpen := db.numOpen |
| 1680 | |
| 1681 | // simulate a broken connection on the first try |
| 1682 | *hook = func() bool { |
| 1683 | if !broken { |
| 1684 | broken = true |
| 1685 | return true |
| 1686 | } |
| 1687 | retried = true |
| 1688 | return false |
| 1689 | } |
| 1690 | |
| 1691 | if err := op(); err != nil { |
| 1692 | t.Errorf(name+": %v", err) |
| 1693 | return |
| 1694 | } |
| 1695 | |
| 1696 | if !broken || !retried { |
| 1697 | t.Error(name + ": Failed to simulate broken connection") |
| 1698 | } |
| 1699 | *hook = nil |
| 1700 | |
| 1701 | if numOpen != db.numOpen { |
| 1702 | t.Errorf(name+": leaked %d connection(s)!", db.numOpen-numOpen) |
| 1703 | numOpen = db.numOpen |
| 1704 | } |
| 1705 | } |
| 1706 | |
| 1707 | // db.Exec |
| 1708 | dbExec := func() error { |
| 1709 | _, err := db.Exec("INSERT|t1|name=?,age=?,dead=?", "Gordon", 3, true) |
| 1710 | return err |
| 1711 | } |
| 1712 | simulateBadConn("db.Exec prepare", &hookPrepareBadConn, dbExec) |
| 1713 | simulateBadConn("db.Exec exec", &hookExecBadConn, dbExec) |
| 1714 | |
| 1715 | // db.Query |
| 1716 | dbQuery := func() error { |
| 1717 | rows, err := db.Query("SELECT|t1|age,name|") |
| 1718 | if err == nil { |
| 1719 | err = rows.Close() |
| 1720 | } |
| 1721 | return err |
| 1722 | } |
| 1723 | simulateBadConn("db.Query prepare", &hookPrepareBadConn, dbQuery) |
| 1724 | simulateBadConn("db.Query query", &hookQueryBadConn, dbQuery) |
| 1725 | |
| 1726 | // db.Prepare |
| 1727 | simulateBadConn("db.Prepare", &hookPrepareBadConn, func() error { |
| 1728 | stmt, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?") |
| 1729 | if err != nil { |
| 1730 | return err |
| 1731 | } |
| 1732 | stmt.Close() |
| 1733 | return nil |
| 1734 | }) |
| 1735 | |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1736 | // Provide a way to force a re-prepare of a statement on next execution |
| 1737 | forcePrepare := func(stmt *Stmt) { |
| 1738 | stmt.css = nil |
| 1739 | } |
| 1740 | |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1741 | // stmt.Exec |
| 1742 | stmt1, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?") |
| 1743 | if err != nil { |
| 1744 | t.Fatalf("prepare: %v", err) |
| 1745 | } |
| 1746 | defer stmt1.Close() |
| 1747 | // make sure we must prepare the stmt first |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1748 | forcePrepare(stmt1) |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1749 | |
| 1750 | stmtExec := func() error { |
| 1751 | _, err := stmt1.Exec("Gopher", 3, false) |
| 1752 | return err |
| 1753 | } |
| 1754 | simulateBadConn("stmt.Exec prepare", &hookPrepareBadConn, stmtExec) |
| 1755 | simulateBadConn("stmt.Exec exec", &hookExecBadConn, stmtExec) |
| 1756 | |
| 1757 | // stmt.Query |
| 1758 | stmt2, err := db.Prepare("SELECT|t1|age,name|") |
| 1759 | if err != nil { |
| 1760 | t.Fatalf("prepare: %v", err) |
| 1761 | } |
| 1762 | defer stmt2.Close() |
| 1763 | // make sure we must prepare the stmt first |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1764 | forcePrepare(stmt2) |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1765 | |
| 1766 | stmtQuery := func() error { |
| 1767 | rows, err := stmt2.Query() |
| 1768 | if err == nil { |
| 1769 | err = rows.Close() |
| 1770 | } |
| 1771 | return err |
| 1772 | } |
| 1773 | simulateBadConn("stmt.Query prepare", &hookPrepareBadConn, stmtQuery) |
| 1774 | simulateBadConn("stmt.Query exec", &hookQueryBadConn, stmtQuery) |
| 1775 | } |
| 1776 | |
Chris Hines | d737639 | 2015-08-24 21:48:39 -0400 | [diff] [blame] | 1777 | // golang.org/issue/11264 |
| 1778 | func TestTxEndBadConn(t *testing.T) { |
| 1779 | db := newTestDB(t, "foo") |
| 1780 | defer closeDB(t, db) |
| 1781 | db.SetMaxIdleConns(0) |
| 1782 | exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") |
| 1783 | db.SetMaxIdleConns(1) |
| 1784 | |
| 1785 | simulateBadConn := func(name string, hook *func() bool, op func() error) { |
| 1786 | broken := false |
| 1787 | numOpen := db.numOpen |
| 1788 | |
| 1789 | *hook = func() bool { |
| 1790 | if !broken { |
| 1791 | broken = true |
| 1792 | } |
| 1793 | return broken |
| 1794 | } |
| 1795 | |
| 1796 | if err := op(); err != driver.ErrBadConn { |
| 1797 | t.Errorf(name+": %v", err) |
| 1798 | return |
| 1799 | } |
| 1800 | |
| 1801 | if !broken { |
| 1802 | t.Error(name + ": Failed to simulate broken connection") |
| 1803 | } |
| 1804 | *hook = nil |
| 1805 | |
| 1806 | if numOpen != db.numOpen { |
| 1807 | t.Errorf(name+": leaked %d connection(s)!", db.numOpen-numOpen) |
| 1808 | } |
| 1809 | } |
| 1810 | |
| 1811 | // db.Exec |
| 1812 | dbExec := func(endTx func(tx *Tx) error) func() error { |
| 1813 | return func() error { |
| 1814 | tx, err := db.Begin() |
| 1815 | if err != nil { |
| 1816 | return err |
| 1817 | } |
| 1818 | _, err = tx.Exec("INSERT|t1|name=?,age=?,dead=?", "Gordon", 3, true) |
| 1819 | if err != nil { |
| 1820 | return err |
| 1821 | } |
| 1822 | return endTx(tx) |
| 1823 | } |
| 1824 | } |
| 1825 | simulateBadConn("db.Tx.Exec commit", &hookCommitBadConn, dbExec((*Tx).Commit)) |
| 1826 | simulateBadConn("db.Tx.Exec rollback", &hookRollbackBadConn, dbExec((*Tx).Rollback)) |
| 1827 | |
| 1828 | // db.Query |
| 1829 | dbQuery := func(endTx func(tx *Tx) error) func() error { |
| 1830 | return func() error { |
| 1831 | tx, err := db.Begin() |
| 1832 | if err != nil { |
| 1833 | return err |
| 1834 | } |
| 1835 | rows, err := tx.Query("SELECT|t1|age,name|") |
| 1836 | if err == nil { |
| 1837 | err = rows.Close() |
| 1838 | } else { |
| 1839 | return err |
| 1840 | } |
| 1841 | return endTx(tx) |
| 1842 | } |
| 1843 | } |
| 1844 | simulateBadConn("db.Tx.Query commit", &hookCommitBadConn, dbQuery((*Tx).Commit)) |
| 1845 | simulateBadConn("db.Tx.Query rollback", &hookRollbackBadConn, dbQuery((*Tx).Rollback)) |
| 1846 | } |
| 1847 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1848 | type concurrentTest interface { |
| 1849 | init(t testing.TB, db *DB) |
| 1850 | finish(t testing.TB) |
| 1851 | test(t testing.TB) error |
| 1852 | } |
| 1853 | |
| 1854 | type concurrentDBQueryTest struct { |
| 1855 | db *DB |
| 1856 | } |
| 1857 | |
| 1858 | func (c *concurrentDBQueryTest) init(t testing.TB, db *DB) { |
| 1859 | c.db = db |
| 1860 | } |
| 1861 | |
| 1862 | func (c *concurrentDBQueryTest) finish(t testing.TB) { |
| 1863 | c.db = nil |
| 1864 | } |
| 1865 | |
| 1866 | func (c *concurrentDBQueryTest) test(t testing.TB) error { |
| 1867 | rows, err := c.db.Query("SELECT|people|name|") |
| 1868 | if err != nil { |
| 1869 | t.Error(err) |
| 1870 | return err |
| 1871 | } |
| 1872 | var name string |
| 1873 | for rows.Next() { |
| 1874 | rows.Scan(&name) |
| 1875 | } |
| 1876 | rows.Close() |
| 1877 | return nil |
| 1878 | } |
| 1879 | |
| 1880 | type concurrentDBExecTest struct { |
| 1881 | db *DB |
| 1882 | } |
| 1883 | |
| 1884 | func (c *concurrentDBExecTest) init(t testing.TB, db *DB) { |
| 1885 | c.db = db |
| 1886 | } |
| 1887 | |
| 1888 | func (c *concurrentDBExecTest) finish(t testing.TB) { |
| 1889 | c.db = nil |
| 1890 | } |
| 1891 | |
| 1892 | func (c *concurrentDBExecTest) test(t testing.TB) error { |
| 1893 | _, err := c.db.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) |
| 1894 | if err != nil { |
| 1895 | t.Error(err) |
| 1896 | return err |
| 1897 | } |
| 1898 | return nil |
| 1899 | } |
| 1900 | |
| 1901 | type concurrentStmtQueryTest struct { |
| 1902 | db *DB |
| 1903 | stmt *Stmt |
| 1904 | } |
| 1905 | |
| 1906 | func (c *concurrentStmtQueryTest) init(t testing.TB, db *DB) { |
| 1907 | c.db = db |
| 1908 | var err error |
| 1909 | c.stmt, err = db.Prepare("SELECT|people|name|") |
| 1910 | if err != nil { |
| 1911 | t.Fatal(err) |
| 1912 | } |
| 1913 | } |
| 1914 | |
| 1915 | func (c *concurrentStmtQueryTest) finish(t testing.TB) { |
| 1916 | if c.stmt != nil { |
| 1917 | c.stmt.Close() |
| 1918 | c.stmt = nil |
| 1919 | } |
| 1920 | c.db = nil |
| 1921 | } |
| 1922 | |
| 1923 | func (c *concurrentStmtQueryTest) test(t testing.TB) error { |
| 1924 | rows, err := c.stmt.Query() |
| 1925 | if err != nil { |
| 1926 | t.Errorf("error on query: %v", err) |
| 1927 | return err |
| 1928 | } |
| 1929 | |
| 1930 | var name string |
| 1931 | for rows.Next() { |
| 1932 | rows.Scan(&name) |
| 1933 | } |
| 1934 | rows.Close() |
| 1935 | return nil |
| 1936 | } |
| 1937 | |
| 1938 | type concurrentStmtExecTest struct { |
| 1939 | db *DB |
| 1940 | stmt *Stmt |
| 1941 | } |
| 1942 | |
| 1943 | func (c *concurrentStmtExecTest) init(t testing.TB, db *DB) { |
| 1944 | c.db = db |
| 1945 | var err error |
| 1946 | c.stmt, err = db.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?") |
| 1947 | if err != nil { |
| 1948 | t.Fatal(err) |
| 1949 | } |
| 1950 | } |
| 1951 | |
| 1952 | func (c *concurrentStmtExecTest) finish(t testing.TB) { |
| 1953 | if c.stmt != nil { |
| 1954 | c.stmt.Close() |
| 1955 | c.stmt = nil |
| 1956 | } |
| 1957 | c.db = nil |
| 1958 | } |
| 1959 | |
| 1960 | func (c *concurrentStmtExecTest) test(t testing.TB) error { |
| 1961 | _, err := c.stmt.Exec(3, chrisBirthday) |
| 1962 | if err != nil { |
| 1963 | t.Errorf("error on exec: %v", err) |
| 1964 | return err |
| 1965 | } |
| 1966 | return nil |
| 1967 | } |
| 1968 | |
| 1969 | type concurrentTxQueryTest struct { |
| 1970 | db *DB |
| 1971 | tx *Tx |
| 1972 | } |
| 1973 | |
| 1974 | func (c *concurrentTxQueryTest) init(t testing.TB, db *DB) { |
| 1975 | c.db = db |
| 1976 | var err error |
| 1977 | c.tx, err = c.db.Begin() |
| 1978 | if err != nil { |
| 1979 | t.Fatal(err) |
| 1980 | } |
| 1981 | } |
| 1982 | |
| 1983 | func (c *concurrentTxQueryTest) finish(t testing.TB) { |
| 1984 | if c.tx != nil { |
| 1985 | c.tx.Rollback() |
| 1986 | c.tx = nil |
| 1987 | } |
| 1988 | c.db = nil |
| 1989 | } |
| 1990 | |
| 1991 | func (c *concurrentTxQueryTest) test(t testing.TB) error { |
| 1992 | rows, err := c.db.Query("SELECT|people|name|") |
| 1993 | if err != nil { |
| 1994 | t.Error(err) |
| 1995 | return err |
| 1996 | } |
| 1997 | var name string |
| 1998 | for rows.Next() { |
| 1999 | rows.Scan(&name) |
| 2000 | } |
| 2001 | rows.Close() |
| 2002 | return nil |
| 2003 | } |
| 2004 | |
| 2005 | type concurrentTxExecTest struct { |
| 2006 | db *DB |
| 2007 | tx *Tx |
| 2008 | } |
| 2009 | |
| 2010 | func (c *concurrentTxExecTest) init(t testing.TB, db *DB) { |
| 2011 | c.db = db |
| 2012 | var err error |
| 2013 | c.tx, err = c.db.Begin() |
| 2014 | if err != nil { |
| 2015 | t.Fatal(err) |
| 2016 | } |
| 2017 | } |
| 2018 | |
| 2019 | func (c *concurrentTxExecTest) finish(t testing.TB) { |
| 2020 | if c.tx != nil { |
| 2021 | c.tx.Rollback() |
| 2022 | c.tx = nil |
| 2023 | } |
| 2024 | c.db = nil |
| 2025 | } |
| 2026 | |
| 2027 | func (c *concurrentTxExecTest) test(t testing.TB) error { |
| 2028 | _, err := c.tx.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) |
| 2029 | if err != nil { |
| 2030 | t.Error(err) |
| 2031 | return err |
| 2032 | } |
| 2033 | return nil |
| 2034 | } |
| 2035 | |
| 2036 | type concurrentTxStmtQueryTest struct { |
| 2037 | db *DB |
| 2038 | tx *Tx |
| 2039 | stmt *Stmt |
| 2040 | } |
| 2041 | |
| 2042 | func (c *concurrentTxStmtQueryTest) init(t testing.TB, db *DB) { |
| 2043 | c.db = db |
| 2044 | var err error |
| 2045 | c.tx, err = c.db.Begin() |
| 2046 | if err != nil { |
| 2047 | t.Fatal(err) |
| 2048 | } |
| 2049 | c.stmt, err = c.tx.Prepare("SELECT|people|name|") |
| 2050 | if err != nil { |
| 2051 | t.Fatal(err) |
| 2052 | } |
| 2053 | } |
| 2054 | |
| 2055 | func (c *concurrentTxStmtQueryTest) finish(t testing.TB) { |
| 2056 | if c.stmt != nil { |
| 2057 | c.stmt.Close() |
| 2058 | c.stmt = nil |
| 2059 | } |
| 2060 | if c.tx != nil { |
| 2061 | c.tx.Rollback() |
| 2062 | c.tx = nil |
| 2063 | } |
| 2064 | c.db = nil |
| 2065 | } |
| 2066 | |
| 2067 | func (c *concurrentTxStmtQueryTest) test(t testing.TB) error { |
| 2068 | rows, err := c.stmt.Query() |
| 2069 | if err != nil { |
| 2070 | t.Errorf("error on query: %v", err) |
| 2071 | return err |
| 2072 | } |
| 2073 | |
| 2074 | var name string |
| 2075 | for rows.Next() { |
| 2076 | rows.Scan(&name) |
| 2077 | } |
| 2078 | rows.Close() |
| 2079 | return nil |
| 2080 | } |
| 2081 | |
| 2082 | type concurrentTxStmtExecTest struct { |
| 2083 | db *DB |
| 2084 | tx *Tx |
| 2085 | stmt *Stmt |
| 2086 | } |
| 2087 | |
| 2088 | func (c *concurrentTxStmtExecTest) init(t testing.TB, db *DB) { |
| 2089 | c.db = db |
| 2090 | var err error |
| 2091 | c.tx, err = c.db.Begin() |
| 2092 | if err != nil { |
| 2093 | t.Fatal(err) |
| 2094 | } |
| 2095 | c.stmt, err = c.tx.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?") |
| 2096 | if err != nil { |
| 2097 | t.Fatal(err) |
| 2098 | } |
| 2099 | } |
| 2100 | |
| 2101 | func (c *concurrentTxStmtExecTest) finish(t testing.TB) { |
| 2102 | if c.stmt != nil { |
| 2103 | c.stmt.Close() |
| 2104 | c.stmt = nil |
| 2105 | } |
| 2106 | if c.tx != nil { |
| 2107 | c.tx.Rollback() |
| 2108 | c.tx = nil |
| 2109 | } |
| 2110 | c.db = nil |
| 2111 | } |
| 2112 | |
| 2113 | func (c *concurrentTxStmtExecTest) test(t testing.TB) error { |
| 2114 | _, err := c.stmt.Exec(3, chrisBirthday) |
| 2115 | if err != nil { |
| 2116 | t.Errorf("error on exec: %v", err) |
| 2117 | return err |
| 2118 | } |
| 2119 | return nil |
| 2120 | } |
| 2121 | |
| 2122 | type concurrentRandomTest struct { |
| 2123 | tests []concurrentTest |
| 2124 | } |
| 2125 | |
| 2126 | func (c *concurrentRandomTest) init(t testing.TB, db *DB) { |
| 2127 | c.tests = []concurrentTest{ |
| 2128 | new(concurrentDBQueryTest), |
| 2129 | new(concurrentDBExecTest), |
| 2130 | new(concurrentStmtQueryTest), |
| 2131 | new(concurrentStmtExecTest), |
| 2132 | new(concurrentTxQueryTest), |
| 2133 | new(concurrentTxExecTest), |
| 2134 | new(concurrentTxStmtQueryTest), |
| 2135 | new(concurrentTxStmtExecTest), |
| 2136 | } |
| 2137 | for _, ct := range c.tests { |
| 2138 | ct.init(t, db) |
| 2139 | } |
| 2140 | } |
| 2141 | |
| 2142 | func (c *concurrentRandomTest) finish(t testing.TB) { |
| 2143 | for _, ct := range c.tests { |
| 2144 | ct.finish(t) |
| 2145 | } |
| 2146 | } |
| 2147 | |
| 2148 | func (c *concurrentRandomTest) test(t testing.TB) error { |
| 2149 | ct := c.tests[rand.Intn(len(c.tests))] |
| 2150 | return ct.test(t) |
| 2151 | } |
| 2152 | |
| 2153 | func doConcurrentTest(t testing.TB, ct concurrentTest) { |
| 2154 | maxProcs, numReqs := 1, 500 |
| 2155 | if testing.Short() { |
| 2156 | maxProcs, numReqs = 4, 50 |
| 2157 | } |
| 2158 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) |
| 2159 | |
| 2160 | db := newTestDB(t, "people") |
| 2161 | defer closeDB(t, db) |
| 2162 | |
| 2163 | ct.init(t, db) |
| 2164 | defer ct.finish(t) |
| 2165 | |
| 2166 | var wg sync.WaitGroup |
| 2167 | wg.Add(numReqs) |
| 2168 | |
| 2169 | reqs := make(chan bool) |
| 2170 | defer close(reqs) |
| 2171 | |
| 2172 | for i := 0; i < maxProcs*2; i++ { |
| 2173 | go func() { |
Robert Griesemer | 8a23c00 | 2014-07-16 16:29:51 -0700 | [diff] [blame] | 2174 | for range reqs { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 2175 | err := ct.test(t) |
| 2176 | if err != nil { |
| 2177 | wg.Done() |
| 2178 | continue |
| 2179 | } |
| 2180 | wg.Done() |
| 2181 | } |
| 2182 | }() |
| 2183 | } |
| 2184 | |
| 2185 | for i := 0; i < numReqs; i++ { |
| 2186 | reqs <- true |
| 2187 | } |
| 2188 | |
| 2189 | wg.Wait() |
| 2190 | } |
| 2191 | |
Brad Fitzpatrick | ca3ed9f | 2013-08-13 14:56:40 -0700 | [diff] [blame] | 2192 | func TestIssue6081(t *testing.T) { |
Brad Fitzpatrick | ca3ed9f | 2013-08-13 14:56:40 -0700 | [diff] [blame] | 2193 | db := newTestDB(t, "people") |
| 2194 | defer closeDB(t, db) |
| 2195 | |
| 2196 | drv := db.driver.(*fakeDriver) |
| 2197 | drv.mu.Lock() |
| 2198 | opens0 := drv.openCount |
| 2199 | closes0 := drv.closeCount |
| 2200 | drv.mu.Unlock() |
| 2201 | |
| 2202 | stmt, err := db.Prepare("SELECT|people|name|") |
| 2203 | if err != nil { |
| 2204 | t.Fatal(err) |
| 2205 | } |
| 2206 | rowsCloseHook = func(rows *Rows, err *error) { |
| 2207 | *err = driver.ErrBadConn |
| 2208 | } |
| 2209 | defer func() { rowsCloseHook = nil }() |
| 2210 | for i := 0; i < 10; i++ { |
| 2211 | rows, err := stmt.Query() |
| 2212 | if err != nil { |
| 2213 | t.Fatal(err) |
| 2214 | } |
| 2215 | rows.Close() |
| 2216 | } |
| 2217 | if n := len(stmt.css); n > 1 { |
| 2218 | t.Errorf("len(css slice) = %d; want <= 1", n) |
| 2219 | } |
| 2220 | stmt.Close() |
| 2221 | if n := len(stmt.css); n != 0 { |
| 2222 | t.Errorf("len(css slice) after Close = %d; want 0", n) |
| 2223 | } |
| 2224 | |
| 2225 | drv.mu.Lock() |
| 2226 | opens := drv.openCount - opens0 |
| 2227 | closes := drv.closeCount - closes0 |
| 2228 | drv.mu.Unlock() |
| 2229 | if opens < 9 { |
| 2230 | t.Errorf("opens = %d; want >= 9", opens) |
| 2231 | } |
| 2232 | if closes < 9 { |
| 2233 | t.Errorf("closes = %d; want >= 9", closes) |
| 2234 | } |
| 2235 | } |
| 2236 | |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 2237 | func TestConcurrency(t *testing.T) { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 2238 | doConcurrentTest(t, new(concurrentDBQueryTest)) |
| 2239 | doConcurrentTest(t, new(concurrentDBExecTest)) |
| 2240 | doConcurrentTest(t, new(concurrentStmtQueryTest)) |
| 2241 | doConcurrentTest(t, new(concurrentStmtExecTest)) |
| 2242 | doConcurrentTest(t, new(concurrentTxQueryTest)) |
| 2243 | doConcurrentTest(t, new(concurrentTxExecTest)) |
| 2244 | doConcurrentTest(t, new(concurrentTxStmtQueryTest)) |
| 2245 | doConcurrentTest(t, new(concurrentTxStmtExecTest)) |
| 2246 | doConcurrentTest(t, new(concurrentRandomTest)) |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 2247 | } |
| 2248 | |
Alberto García Hierro | 37db880 | 2013-10-16 09:22:57 -0700 | [diff] [blame] | 2249 | func TestConnectionLeak(t *testing.T) { |
| 2250 | db := newTestDB(t, "people") |
| 2251 | defer closeDB(t, db) |
| 2252 | // Start by opening defaultMaxIdleConns |
| 2253 | rows := make([]*Rows, defaultMaxIdleConns) |
| 2254 | // We need to SetMaxOpenConns > MaxIdleConns, so the DB can open |
| 2255 | // a new connection and we can fill the idle queue with the released |
| 2256 | // connections. |
| 2257 | db.SetMaxOpenConns(len(rows) + 1) |
| 2258 | for ii := range rows { |
| 2259 | r, err := db.Query("SELECT|people|name|") |
| 2260 | if err != nil { |
| 2261 | t.Fatal(err) |
| 2262 | } |
| 2263 | r.Next() |
| 2264 | if err := r.Err(); err != nil { |
| 2265 | t.Fatal(err) |
| 2266 | } |
| 2267 | rows[ii] = r |
| 2268 | } |
| 2269 | // Now we have defaultMaxIdleConns busy connections. Open |
| 2270 | // a new one, but wait until the busy connections are released |
| 2271 | // before returning control to DB. |
| 2272 | drv := db.driver.(*fakeDriver) |
| 2273 | drv.waitCh = make(chan struct{}, 1) |
| 2274 | drv.waitingCh = make(chan struct{}, 1) |
| 2275 | var wg sync.WaitGroup |
| 2276 | wg.Add(1) |
| 2277 | go func() { |
| 2278 | r, err := db.Query("SELECT|people|name|") |
| 2279 | if err != nil { |
| 2280 | t.Fatal(err) |
| 2281 | } |
| 2282 | r.Close() |
| 2283 | wg.Done() |
| 2284 | }() |
| 2285 | // Wait until the goroutine we've just created has started waiting. |
| 2286 | <-drv.waitingCh |
| 2287 | // Now close the busy connections. This provides a connection for |
| 2288 | // the blocked goroutine and then fills up the idle queue. |
| 2289 | for _, v := range rows { |
| 2290 | v.Close() |
| 2291 | } |
| 2292 | // At this point we give the new connection to DB. This connection is |
| 2293 | // now useless, since the idle queue is full and there are no pending |
| 2294 | // requests. DB should deal with this situation without leaking the |
| 2295 | // connection. |
| 2296 | drv.waitCh <- struct{}{} |
| 2297 | wg.Wait() |
| 2298 | } |
| 2299 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 2300 | func BenchmarkConcurrentDBExec(b *testing.B) { |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 2301 | b.ReportAllocs() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 2302 | ct := new(concurrentDBExecTest) |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 2303 | for i := 0; i < b.N; i++ { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 2304 | doConcurrentTest(b, ct) |
| 2305 | } |
| 2306 | } |
| 2307 | |
| 2308 | func BenchmarkConcurrentStmtQuery(b *testing.B) { |
| 2309 | b.ReportAllocs() |
| 2310 | ct := new(concurrentStmtQueryTest) |
| 2311 | for i := 0; i < b.N; i++ { |
| 2312 | doConcurrentTest(b, ct) |
| 2313 | } |
| 2314 | } |
| 2315 | |
| 2316 | func BenchmarkConcurrentStmtExec(b *testing.B) { |
| 2317 | b.ReportAllocs() |
| 2318 | ct := new(concurrentStmtExecTest) |
| 2319 | for i := 0; i < b.N; i++ { |
| 2320 | doConcurrentTest(b, ct) |
| 2321 | } |
| 2322 | } |
| 2323 | |
| 2324 | func BenchmarkConcurrentTxQuery(b *testing.B) { |
| 2325 | b.ReportAllocs() |
| 2326 | ct := new(concurrentTxQueryTest) |
| 2327 | for i := 0; i < b.N; i++ { |
| 2328 | doConcurrentTest(b, ct) |
| 2329 | } |
| 2330 | } |
| 2331 | |
| 2332 | func BenchmarkConcurrentTxExec(b *testing.B) { |
| 2333 | b.ReportAllocs() |
| 2334 | ct := new(concurrentTxExecTest) |
| 2335 | for i := 0; i < b.N; i++ { |
| 2336 | doConcurrentTest(b, ct) |
| 2337 | } |
| 2338 | } |
| 2339 | |
| 2340 | func BenchmarkConcurrentTxStmtQuery(b *testing.B) { |
| 2341 | b.ReportAllocs() |
| 2342 | ct := new(concurrentTxStmtQueryTest) |
| 2343 | for i := 0; i < b.N; i++ { |
| 2344 | doConcurrentTest(b, ct) |
| 2345 | } |
| 2346 | } |
| 2347 | |
| 2348 | func BenchmarkConcurrentTxStmtExec(b *testing.B) { |
| 2349 | b.ReportAllocs() |
| 2350 | ct := new(concurrentTxStmtExecTest) |
| 2351 | for i := 0; i < b.N; i++ { |
| 2352 | doConcurrentTest(b, ct) |
| 2353 | } |
| 2354 | } |
| 2355 | |
| 2356 | func BenchmarkConcurrentRandom(b *testing.B) { |
| 2357 | b.ReportAllocs() |
| 2358 | ct := new(concurrentRandomTest) |
| 2359 | for i := 0; i < b.N; i++ { |
| 2360 | doConcurrentTest(b, ct) |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 2361 | } |
| 2362 | } |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 2363 | |
| 2364 | func BenchmarkManyConcurrentQueries(b *testing.B) { |
| 2365 | b.ReportAllocs() |
| 2366 | // To see lock contention in Go 1.4, 16~ cores and 128~ goroutines are required. |
| 2367 | const parallelism = 16 |
| 2368 | |
| 2369 | db := newTestDB(b, "magicquery") |
| 2370 | defer closeDB(b, db) |
| 2371 | db.SetMaxIdleConns(runtime.GOMAXPROCS(0) * parallelism) |
| 2372 | |
| 2373 | stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") |
| 2374 | if err != nil { |
| 2375 | b.Fatal(err) |
| 2376 | } |
| 2377 | defer stmt.Close() |
| 2378 | |
| 2379 | b.SetParallelism(parallelism) |
| 2380 | b.RunParallel(func(pb *testing.PB) { |
| 2381 | for pb.Next() { |
| 2382 | rows, err := stmt.Query("sleep", 1) |
| 2383 | if err != nil { |
| 2384 | b.Error(err) |
| 2385 | return |
| 2386 | } |
| 2387 | rows.Close() |
| 2388 | } |
| 2389 | }) |
| 2390 | } |