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 provides a generic interface around SQL (or SQL-like) |
| 6 | // databases. |
Brad Fitzpatrick | eee3e63 | 2013-03-25 17:38:51 -0700 | [diff] [blame] | 7 | // |
| 8 | // The sql package must be used in conjunction with a database driver. |
Brad Fitzpatrick | 2ae7737 | 2015-07-10 17:17:11 -0600 | [diff] [blame] | 9 | // See https://golang.org/s/sqldrivers for a list of drivers. |
Matthew Cottingham | 17a03d8 | 2013-10-24 10:13:23 -0700 | [diff] [blame] | 10 | // |
| 11 | // For more usage examples, see the wiki page at |
Brad Fitzpatrick | 2ae7737 | 2015-07-10 17:17:11 -0600 | [diff] [blame] | 12 | // https://golang.org/s/sqlwiki. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 13 | package sql |
| 14 | |
| 15 | import ( |
Brad Fitzpatrick | 7fc4c07 | 2012-01-19 16:04:26 -0800 | [diff] [blame] | 16 | "database/sql/driver" |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 17 | "errors" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 18 | "fmt" |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 19 | "io" |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 20 | "runtime" |
Russ Cox | 5318a1b | 2014-10-15 13:10:14 -0400 | [diff] [blame] | 21 | "sort" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 22 | "sync" |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 23 | "sync/atomic" |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 24 | ) |
| 25 | |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 26 | var ( |
| 27 | driversMu sync.Mutex |
| 28 | drivers = make(map[string]driver.Driver) |
| 29 | ) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 30 | |
| 31 | // Register makes a database driver available by the provided name. |
| 32 | // If Register is called twice with the same name or if driver is nil, |
| 33 | // it panics. |
| 34 | func Register(name string, driver driver.Driver) { |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 35 | driversMu.Lock() |
| 36 | defer driversMu.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 37 | if driver == nil { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 38 | panic("sql: Register driver is nil") |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 39 | } |
| 40 | if _, dup := drivers[name]; dup { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 41 | panic("sql: Register called twice for driver " + name) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 42 | } |
| 43 | drivers[name] = driver |
| 44 | } |
| 45 | |
Brad Fitzpatrick | 9dc1cce | 2014-10-31 09:49:42 -0700 | [diff] [blame] | 46 | func unregisterAllDrivers() { |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 47 | driversMu.Lock() |
| 48 | defer driversMu.Unlock() |
Brad Fitzpatrick | 9dc1cce | 2014-10-31 09:49:42 -0700 | [diff] [blame] | 49 | // For tests. |
| 50 | drivers = make(map[string]driver.Driver) |
| 51 | } |
| 52 | |
Russ Cox | 5318a1b | 2014-10-15 13:10:14 -0400 | [diff] [blame] | 53 | // Drivers returns a sorted list of the names of the registered drivers. |
| 54 | func Drivers() []string { |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 55 | driversMu.Lock() |
| 56 | defer driversMu.Unlock() |
Russ Cox | 5318a1b | 2014-10-15 13:10:14 -0400 | [diff] [blame] | 57 | var list []string |
| 58 | for name := range drivers { |
| 59 | list = append(list, name) |
| 60 | } |
| 61 | sort.Strings(list) |
| 62 | return list |
| 63 | } |
| 64 | |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 65 | // RawBytes is a byte slice that holds a reference to memory owned by |
| 66 | // the database itself. After a Scan into a RawBytes, the slice is only |
| 67 | // valid until the next call to Next, Scan, or Close. |
| 68 | type RawBytes []byte |
| 69 | |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 70 | // NullString represents a string that may be null. |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 71 | // NullString implements the Scanner interface so |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 72 | // it can be used as a scan destination: |
| 73 | // |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 74 | // var s NullString |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 75 | // err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s) |
| 76 | // ... |
| 77 | // if s.Valid { |
| 78 | // // use s.String |
| 79 | // } else { |
| 80 | // // NULL value |
| 81 | // } |
| 82 | // |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 83 | type NullString struct { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 84 | String string |
| 85 | Valid bool // Valid is true if String is not NULL |
| 86 | } |
| 87 | |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 88 | // Scan implements the Scanner interface. |
| 89 | func (ns *NullString) Scan(value interface{}) error { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 90 | if value == nil { |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 91 | ns.String, ns.Valid = "", false |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 92 | return nil |
| 93 | } |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 94 | ns.Valid = true |
| 95 | return convertAssign(&ns.String, value) |
| 96 | } |
| 97 | |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 98 | // Value implements the driver Valuer interface. |
| 99 | func (ns NullString) Value() (driver.Value, error) { |
Brad Fitzpatrick | bc0139b | 2012-01-19 09:27:45 -0800 | [diff] [blame] | 100 | if !ns.Valid { |
| 101 | return nil, nil |
| 102 | } |
| 103 | return ns.String, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 104 | } |
| 105 | |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 106 | // NullInt64 represents an int64 that may be null. |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 107 | // NullInt64 implements the Scanner interface so |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 108 | // it can be used as a scan destination, similar to NullString. |
| 109 | type NullInt64 struct { |
| 110 | Int64 int64 |
| 111 | Valid bool // Valid is true if Int64 is not NULL |
| 112 | } |
| 113 | |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 114 | // Scan implements the Scanner interface. |
| 115 | func (n *NullInt64) Scan(value interface{}) error { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 116 | if value == nil { |
| 117 | n.Int64, n.Valid = 0, false |
| 118 | return nil |
| 119 | } |
| 120 | n.Valid = true |
| 121 | return convertAssign(&n.Int64, value) |
| 122 | } |
| 123 | |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 124 | // Value implements the driver Valuer interface. |
| 125 | func (n NullInt64) Value() (driver.Value, error) { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 126 | if !n.Valid { |
| 127 | return nil, nil |
| 128 | } |
| 129 | return n.Int64, nil |
| 130 | } |
| 131 | |
| 132 | // NullFloat64 represents a float64 that may be null. |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 133 | // NullFloat64 implements the Scanner interface so |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 134 | // it can be used as a scan destination, similar to NullString. |
| 135 | type NullFloat64 struct { |
| 136 | Float64 float64 |
| 137 | Valid bool // Valid is true if Float64 is not NULL |
| 138 | } |
| 139 | |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 140 | // Scan implements the Scanner interface. |
| 141 | func (n *NullFloat64) Scan(value interface{}) error { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 142 | if value == nil { |
| 143 | n.Float64, n.Valid = 0, false |
| 144 | return nil |
| 145 | } |
| 146 | n.Valid = true |
| 147 | return convertAssign(&n.Float64, value) |
| 148 | } |
| 149 | |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 150 | // Value implements the driver Valuer interface. |
| 151 | func (n NullFloat64) Value() (driver.Value, error) { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 152 | if !n.Valid { |
| 153 | return nil, nil |
| 154 | } |
| 155 | return n.Float64, nil |
| 156 | } |
| 157 | |
| 158 | // NullBool represents a bool that may be null. |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 159 | // NullBool implements the Scanner interface so |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 160 | // it can be used as a scan destination, similar to NullString. |
| 161 | type NullBool struct { |
| 162 | Bool bool |
| 163 | Valid bool // Valid is true if Bool is not NULL |
| 164 | } |
| 165 | |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 166 | // Scan implements the Scanner interface. |
| 167 | func (n *NullBool) Scan(value interface{}) error { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 168 | if value == nil { |
| 169 | n.Bool, n.Valid = false, false |
| 170 | return nil |
| 171 | } |
| 172 | n.Valid = true |
| 173 | return convertAssign(&n.Bool, value) |
| 174 | } |
| 175 | |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 176 | // Value implements the driver Valuer interface. |
| 177 | func (n NullBool) Value() (driver.Value, error) { |
James P. Cooper | c21b343 | 2012-01-25 17:47:32 -0800 | [diff] [blame] | 178 | if !n.Valid { |
| 179 | return nil, nil |
| 180 | } |
| 181 | return n.Bool, nil |
| 182 | } |
| 183 | |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 184 | // Scanner is an interface used by Scan. |
| 185 | type Scanner interface { |
| 186 | // Scan assigns a value from a database driver. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 187 | // |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 188 | // The src value will be of one of the following restricted |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 189 | // set of types: |
| 190 | // |
| 191 | // int64 |
| 192 | // float64 |
| 193 | // bool |
| 194 | // []byte |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 195 | // string |
| 196 | // time.Time |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 197 | // nil - for NULL values |
| 198 | // |
| 199 | // An error should be returned if the value can not be stored |
| 200 | // without loss of information. |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 201 | Scan(src interface{}) error |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | // ErrNoRows is returned by Scan when QueryRow doesn't return a |
| 205 | // row. In such a case, QueryRow returns a placeholder *Row value that |
| 206 | // defers this error until a Scan. |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 207 | var ErrNoRows = errors.New("sql: no rows in result set") |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 208 | |
Brad Fitzpatrick | 7b103c5 | 2014-05-19 09:54:47 -0700 | [diff] [blame] | 209 | // DB is a database handle representing a pool of zero or more |
| 210 | // underlying connections. It's safe for concurrent use by multiple |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 211 | // goroutines. |
Brad Fitzpatrick | 502e29f | 2012-03-06 17:44:47 -0800 | [diff] [blame] | 212 | // |
Brad Fitzpatrick | 646e541 | 2013-03-18 15:54:22 -0700 | [diff] [blame] | 213 | // The sql package creates and frees connections automatically; it |
| 214 | // also maintains a free pool of idle connections. If the database has |
| 215 | // a concept of per-connection state, such state can only be reliably |
| 216 | // observed within a transaction. Once DB.Begin is called, the |
| 217 | // returned Tx is bound to a single connection. Once Commit or |
| 218 | // Rollback is called on the transaction, that transaction's |
| 219 | // connection is returned to DB's idle connection pool. The pool size |
| 220 | // can be controlled with SetMaxIdleConns. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 221 | type DB struct { |
| 222 | driver driver.Driver |
| 223 | dsn string |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 224 | // numClosed is an atomic counter which represents a total number of |
| 225 | // closed connections. Stmt.openStmt checks it before cleaning closed |
| 226 | // connections in Stmt.css. |
| 227 | numClosed uint64 |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 228 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 229 | mu sync.Mutex // protects following fields |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 230 | freeConn []*driverConn |
Brad Fitzpatrick | 558bd8e | 2014-08-28 11:07:29 -0700 | [diff] [blame] | 231 | connRequests []chan connRequest |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 232 | numOpen int |
| 233 | pendingOpens int |
Julien Schmidt | e6c4fa58 | 2013-10-29 16:03:13 -0700 | [diff] [blame] | 234 | // Used to signal the need for new connections |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 235 | // a goroutine running connectionOpener() reads on this chan and |
| 236 | // maybeOpenNewConnections sends on the chan (one send per needed connection) |
| 237 | // It is closed during db.Close(). The close tells the connectionOpener |
| 238 | // goroutine to exit. |
| 239 | openerCh chan struct{} |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 240 | closed bool |
| 241 | dep map[finalCloser]depSet |
| 242 | lastPut map[*driverConn]string // stacktrace of last conn's put; debug only |
| 243 | maxIdle int // zero means defaultMaxIdleConns; negative means 0 |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 244 | maxOpen int // <= 0 means unlimited |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 245 | } |
| 246 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 247 | // connReuseStrategy determines how (*DB).conn returns database connections. |
| 248 | type connReuseStrategy uint8 |
| 249 | |
| 250 | const ( |
| 251 | // alwaysNewConn forces a new connection to the database. |
| 252 | alwaysNewConn connReuseStrategy = iota |
| 253 | // cachedOrNewConn returns a cached connection, if available, else waits |
| 254 | // for one to become available (if MaxOpenConns has been reached) or |
| 255 | // creates a new database connection. |
| 256 | cachedOrNewConn |
| 257 | ) |
| 258 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 259 | // driverConn wraps a driver.Conn with a mutex, to |
| 260 | // be held during all calls into the Conn. (including any calls onto |
| 261 | // interfaces returned via that Conn, such as calls on Tx, Stmt, |
| 262 | // Result, Rows) |
| 263 | type driverConn struct { |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 264 | db *DB |
| 265 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 266 | sync.Mutex // guards following |
| 267 | ci driver.Conn |
| 268 | closed bool |
| 269 | finalClosed bool // ci.Close has been called |
| 270 | openStmt map[driver.Stmt]bool |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 271 | |
| 272 | // guarded by db.mu |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 273 | inUse bool |
| 274 | onPut []func() // code (with db.mu held) run when conn is next returned |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 275 | dbmuClosed bool // same as closed, but guarded by db.mu, for removeClosedStmtLocked |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 276 | } |
| 277 | |
Brad Fitzpatrick | 0bbf0ec | 2013-05-14 16:35:31 -0700 | [diff] [blame] | 278 | func (dc *driverConn) releaseConn(err error) { |
| 279 | dc.db.putConn(dc, err) |
| 280 | } |
| 281 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 282 | func (dc *driverConn) removeOpenStmt(si driver.Stmt) { |
| 283 | dc.Lock() |
| 284 | defer dc.Unlock() |
| 285 | delete(dc.openStmt, si) |
| 286 | } |
| 287 | |
| 288 | func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) { |
| 289 | si, err := dc.ci.Prepare(query) |
| 290 | if err == nil { |
| 291 | // Track each driverConn's open statements, so we can close them |
| 292 | // before closing the conn. |
| 293 | // |
| 294 | // TODO(bradfitz): let drivers opt out of caring about |
| 295 | // stmt closes if the conn is about to close anyway? For now |
| 296 | // do the safe thing, in case stmts need to be closed. |
| 297 | // |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 298 | // TODO(bradfitz): after Go 1.2, closing driver.Stmts |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 299 | // should be moved to driverStmt, using unique |
| 300 | // *driverStmts everywhere (including from |
| 301 | // *Stmt.connStmt, instead of returning a |
| 302 | // driver.Stmt), using driverStmt as a pointer |
| 303 | // everywhere, and making it a finalCloser. |
| 304 | if dc.openStmt == nil { |
| 305 | dc.openStmt = make(map[driver.Stmt]bool) |
| 306 | } |
| 307 | dc.openStmt[si] = true |
| 308 | } |
| 309 | return si, err |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 310 | } |
| 311 | |
| 312 | // the dc.db's Mutex is held. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 313 | func (dc *driverConn) closeDBLocked() func() error { |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 314 | dc.Lock() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 315 | defer dc.Unlock() |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 316 | if dc.closed { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 317 | return func() error { return errors.New("sql: duplicate driverConn close") } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 318 | } |
| 319 | dc.closed = true |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 320 | return dc.db.removeDepLocked(dc, dc) |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | func (dc *driverConn) Close() error { |
| 324 | dc.Lock() |
| 325 | if dc.closed { |
| 326 | dc.Unlock() |
| 327 | return errors.New("sql: duplicate driverConn close") |
| 328 | } |
| 329 | dc.closed = true |
| 330 | dc.Unlock() // not defer; removeDep finalClose calls may need to lock |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 331 | |
| 332 | // And now updates that require holding dc.mu.Lock. |
| 333 | dc.db.mu.Lock() |
| 334 | dc.dbmuClosed = true |
| 335 | fn := dc.db.removeDepLocked(dc, dc) |
| 336 | dc.db.mu.Unlock() |
| 337 | return fn() |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 338 | } |
| 339 | |
| 340 | func (dc *driverConn) finalClose() error { |
| 341 | dc.Lock() |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 342 | |
| 343 | for si := range dc.openStmt { |
| 344 | si.Close() |
| 345 | } |
| 346 | dc.openStmt = nil |
| 347 | |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 348 | err := dc.ci.Close() |
| 349 | dc.ci = nil |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 350 | dc.finalClosed = true |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 351 | dc.Unlock() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 352 | |
| 353 | dc.db.mu.Lock() |
| 354 | dc.db.numOpen-- |
| 355 | dc.db.maybeOpenNewConnections() |
| 356 | dc.db.mu.Unlock() |
| 357 | |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 358 | atomic.AddUint64(&dc.db.numClosed, 1) |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 359 | return err |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 360 | } |
| 361 | |
| 362 | // driverStmt associates a driver.Stmt with the |
| 363 | // *driverConn from which it came, so the driverConn's lock can be |
| 364 | // held during calls. |
| 365 | type driverStmt struct { |
| 366 | sync.Locker // the *driverConn |
| 367 | si driver.Stmt |
| 368 | } |
| 369 | |
| 370 | func (ds *driverStmt) Close() error { |
| 371 | ds.Lock() |
| 372 | defer ds.Unlock() |
| 373 | return ds.si.Close() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 374 | } |
| 375 | |
| 376 | // depSet is a finalCloser's outstanding dependencies |
| 377 | type depSet map[interface{}]bool // set of true bools |
| 378 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 379 | // The finalCloser interface is used by (*DB).addDep and related |
| 380 | // dependency reference counting. |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 381 | type finalCloser interface { |
| 382 | // finalClose is called when the reference count of an object |
| 383 | // goes to zero. (*DB).mu is not held while calling it. |
| 384 | finalClose() error |
| 385 | } |
| 386 | |
| 387 | // addDep notes that x now depends on dep, and x's finalClose won't be |
| 388 | // called until all of x's dependencies are removed with removeDep. |
| 389 | func (db *DB) addDep(x finalCloser, dep interface{}) { |
| 390 | //println(fmt.Sprintf("addDep(%T %p, %T %p)", x, x, dep, dep)) |
| 391 | db.mu.Lock() |
| 392 | defer db.mu.Unlock() |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 393 | db.addDepLocked(x, dep) |
| 394 | } |
| 395 | |
| 396 | func (db *DB) addDepLocked(x finalCloser, dep interface{}) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 397 | if db.dep == nil { |
| 398 | db.dep = make(map[finalCloser]depSet) |
| 399 | } |
| 400 | xdep := db.dep[x] |
| 401 | if xdep == nil { |
| 402 | xdep = make(depSet) |
| 403 | db.dep[x] = xdep |
| 404 | } |
| 405 | xdep[dep] = true |
| 406 | } |
| 407 | |
| 408 | // removeDep notes that x no longer depends on dep. |
| 409 | // If x still has dependencies, nil is returned. |
| 410 | // If x no longer has any dependencies, its finalClose method will be |
| 411 | // called and its error value will be returned. |
| 412 | func (db *DB) removeDep(x finalCloser, dep interface{}) error { |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 413 | db.mu.Lock() |
| 414 | fn := db.removeDepLocked(x, dep) |
| 415 | db.mu.Unlock() |
| 416 | return fn() |
| 417 | } |
| 418 | |
| 419 | func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 420 | //println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep)) |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 421 | |
Brad Fitzpatrick | 0e10196 | 2013-05-21 14:58:08 -0700 | [diff] [blame] | 422 | xdep, ok := db.dep[x] |
| 423 | if !ok { |
| 424 | panic(fmt.Sprintf("unpaired removeDep: no deps for %T", x)) |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 425 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 426 | |
Brad Fitzpatrick | 0e10196 | 2013-05-21 14:58:08 -0700 | [diff] [blame] | 427 | l0 := len(xdep) |
| 428 | delete(xdep, dep) |
| 429 | |
| 430 | switch len(xdep) { |
| 431 | case l0: |
| 432 | // Nothing removed. Shouldn't happen. |
| 433 | panic(fmt.Sprintf("unpaired removeDep: no %T dep on %T", dep, x)) |
| 434 | case 0: |
| 435 | // No more dependencies. |
| 436 | delete(db.dep, x) |
| 437 | return x.finalClose |
| 438 | default: |
| 439 | // Dependencies remain. |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 440 | return func() error { return nil } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 441 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 442 | } |
| 443 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 444 | // This is the size of the connectionOpener request chan (dn.openerCh). |
| 445 | // This value should be larger than the maximum typical value |
| 446 | // used for db.maxOpen. If maxOpen is significantly larger than |
| 447 | // connectionRequestQueueSize then it is possible for ALL calls into the *DB |
Robert Hencke | f999e14 | 2014-04-29 12:44:40 -0400 | [diff] [blame] | 448 | // to block until the connectionOpener can satisfy the backlog of requests. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 449 | var connectionRequestQueueSize = 1000000 |
| 450 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 451 | // Open opens a database specified by its database driver name and a |
| 452 | // driver-specific data source name, usually consisting of at least a |
| 453 | // database name and connection information. |
| 454 | // |
| 455 | // Most users will open a database via a driver-specific connection |
Brad Fitzpatrick | eee3e63 | 2013-03-25 17:38:51 -0700 | [diff] [blame] | 456 | // helper function that returns a *DB. No database drivers are included |
Brad Fitzpatrick | 2ae7737 | 2015-07-10 17:17:11 -0600 | [diff] [blame] | 457 | // in the Go standard library. See https://golang.org/s/sqldrivers for |
Brad Fitzpatrick | eee3e63 | 2013-03-25 17:38:51 -0700 | [diff] [blame] | 458 | // a list of third-party drivers. |
Brad Fitzpatrick | a4a8651 | 2013-03-14 14:06:46 -0700 | [diff] [blame] | 459 | // |
| 460 | // Open may just validate its arguments without creating a connection |
| 461 | // to the database. To verify that the data source name is valid, call |
| 462 | // Ping. |
Brad Fitzpatrick | 7b103c5 | 2014-05-19 09:54:47 -0700 | [diff] [blame] | 463 | // |
| 464 | // The returned DB is safe for concurrent use by multiple goroutines |
| 465 | // and maintains its own pool of idle connections. Thus, the Open |
| 466 | // function should be called just once. It is rarely necessary to |
| 467 | // close a DB. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 468 | func Open(driverName, dataSourceName string) (*DB, error) { |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 469 | driversMu.Lock() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 470 | driveri, ok := drivers[driverName] |
Brad Fitzpatrick | fc2eee8 | 2015-06-29 17:56:20 -0700 | [diff] [blame] | 471 | driversMu.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 472 | if !ok { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 473 | return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 474 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 475 | db := &DB{ |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 476 | driver: driveri, |
| 477 | dsn: dataSourceName, |
| 478 | openerCh: make(chan struct{}, connectionRequestQueueSize), |
| 479 | lastPut: make(map[*driverConn]string), |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 480 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 481 | go db.connectionOpener() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 482 | return db, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 483 | } |
| 484 | |
Brad Fitzpatrick | a4a8651 | 2013-03-14 14:06:46 -0700 | [diff] [blame] | 485 | // Ping verifies a connection to the database is still alive, |
| 486 | // establishing a connection if necessary. |
| 487 | func (db *DB) Ping() error { |
| 488 | // TODO(bradfitz): give drivers an optional hook to implement |
| 489 | // this in a more efficient or more reliable way, if they |
| 490 | // have one. |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 491 | dc, err := db.conn(cachedOrNewConn) |
Brad Fitzpatrick | a4a8651 | 2013-03-14 14:06:46 -0700 | [diff] [blame] | 492 | if err != nil { |
| 493 | return err |
| 494 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 495 | db.putConn(dc, nil) |
Brad Fitzpatrick | a4a8651 | 2013-03-14 14:06:46 -0700 | [diff] [blame] | 496 | return nil |
| 497 | } |
| 498 | |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 499 | // Close closes the database, releasing any open resources. |
Brad Fitzpatrick | 7b103c5 | 2014-05-19 09:54:47 -0700 | [diff] [blame] | 500 | // |
| 501 | // It is rare to Close a DB, as the DB handle is meant to be |
| 502 | // long-lived and shared between many goroutines. |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 503 | func (db *DB) Close() error { |
| 504 | db.mu.Lock() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 505 | if db.closed { // Make DB.Close idempotent |
| 506 | db.mu.Unlock() |
| 507 | return nil |
| 508 | } |
| 509 | close(db.openerCh) |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 510 | var err error |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 511 | fns := make([]func() error, 0, len(db.freeConn)) |
| 512 | for _, dc := range db.freeConn { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 513 | fns = append(fns, dc.closeDBLocked()) |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 514 | } |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 515 | db.freeConn = nil |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 516 | db.closed = true |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 517 | for _, req := range db.connRequests { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 518 | close(req) |
| 519 | } |
| 520 | db.mu.Unlock() |
| 521 | for _, fn := range fns { |
| 522 | err1 := fn() |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 523 | if err1 != nil { |
| 524 | err = err1 |
| 525 | } |
| 526 | } |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 527 | return err |
| 528 | } |
| 529 | |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 530 | const defaultMaxIdleConns = 2 |
| 531 | |
| 532 | func (db *DB) maxIdleConnsLocked() int { |
| 533 | n := db.maxIdle |
| 534 | switch { |
| 535 | case n == 0: |
| 536 | // TODO(bradfitz): ask driver, if supported, for its default preference |
| 537 | return defaultMaxIdleConns |
| 538 | case n < 0: |
| 539 | return 0 |
| 540 | default: |
| 541 | return n |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | // SetMaxIdleConns sets the maximum number of connections in the idle |
| 546 | // connection pool. |
| 547 | // |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 548 | // If MaxOpenConns is greater than 0 but less than the new MaxIdleConns |
| 549 | // then the new MaxIdleConns will be reduced to match the MaxOpenConns limit |
| 550 | // |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 551 | // If n <= 0, no idle connections are retained. |
| 552 | func (db *DB) SetMaxIdleConns(n int) { |
| 553 | db.mu.Lock() |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 554 | if n > 0 { |
| 555 | db.maxIdle = n |
| 556 | } else { |
| 557 | // No idle connections. |
| 558 | db.maxIdle = -1 |
| 559 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 560 | // Make sure maxIdle doesn't exceed maxOpen |
| 561 | if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen { |
| 562 | db.maxIdle = db.maxOpen |
| 563 | } |
Alberto GarcĂa Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 564 | var closing []*driverConn |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 565 | idleCount := len(db.freeConn) |
| 566 | maxIdle := db.maxIdleConnsLocked() |
| 567 | if idleCount > maxIdle { |
| 568 | closing = db.freeConn[maxIdle:] |
| 569 | db.freeConn = db.freeConn[:maxIdle] |
Alberto GarcĂa Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 570 | } |
| 571 | db.mu.Unlock() |
| 572 | for _, c := range closing { |
| 573 | c.Close() |
Brad Fitzpatrick | 3a2fe62 | 2013-03-18 15:33:04 -0700 | [diff] [blame] | 574 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 575 | } |
| 576 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 577 | // SetMaxOpenConns sets the maximum number of open connections to the database. |
| 578 | // |
| 579 | // If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than |
| 580 | // MaxIdleConns, then MaxIdleConns will be reduced to match the new |
| 581 | // MaxOpenConns limit |
| 582 | // |
| 583 | // If n <= 0, then there is no limit on the number of open connections. |
| 584 | // The default is 0 (unlimited). |
| 585 | func (db *DB) SetMaxOpenConns(n int) { |
| 586 | db.mu.Lock() |
| 587 | db.maxOpen = n |
| 588 | if n < 0 { |
| 589 | db.maxOpen = 0 |
| 590 | } |
| 591 | syncMaxIdle := db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen |
| 592 | db.mu.Unlock() |
| 593 | if syncMaxIdle { |
| 594 | db.SetMaxIdleConns(n) |
| 595 | } |
| 596 | } |
| 597 | |
Andrei Korzhevskii | 297c1d2 | 2015-03-23 18:23:53 +0300 | [diff] [blame] | 598 | // DBStats contains database statistics. |
| 599 | type DBStats struct { |
| 600 | // OpenConnections is the number of open connections to the database. |
| 601 | OpenConnections int |
| 602 | } |
| 603 | |
| 604 | // Stats returns database statistics. |
| 605 | func (db *DB) Stats() DBStats { |
| 606 | db.mu.Lock() |
| 607 | stats := DBStats{ |
| 608 | OpenConnections: db.numOpen, |
| 609 | } |
| 610 | db.mu.Unlock() |
| 611 | return stats |
| 612 | } |
| 613 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 614 | // Assumes db.mu is locked. |
| 615 | // If there are connRequests and the connection limit hasn't been reached, |
| 616 | // then tell the connectionOpener to open new connections. |
| 617 | func (db *DB) maybeOpenNewConnections() { |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 618 | numRequests := len(db.connRequests) - db.pendingOpens |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 619 | if db.maxOpen > 0 { |
| 620 | numCanOpen := db.maxOpen - (db.numOpen + db.pendingOpens) |
| 621 | if numRequests > numCanOpen { |
| 622 | numRequests = numCanOpen |
| 623 | } |
| 624 | } |
| 625 | for numRequests > 0 { |
| 626 | db.pendingOpens++ |
| 627 | numRequests-- |
| 628 | db.openerCh <- struct{}{} |
| 629 | } |
| 630 | } |
| 631 | |
Martin Olsson | 5499034 | 2013-12-27 08:59:02 -0800 | [diff] [blame] | 632 | // Runs in a separate goroutine, opens new connections when requested. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 633 | func (db *DB) connectionOpener() { |
Robert Griesemer | 8a23c00 | 2014-07-16 16:29:51 -0700 | [diff] [blame] | 634 | for range db.openerCh { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 635 | db.openNewConnection() |
| 636 | } |
| 637 | } |
| 638 | |
| 639 | // Open one new connection |
| 640 | func (db *DB) openNewConnection() { |
| 641 | ci, err := db.driver.Open(db.dsn) |
| 642 | db.mu.Lock() |
| 643 | defer db.mu.Unlock() |
| 644 | if db.closed { |
| 645 | if err == nil { |
| 646 | ci.Close() |
| 647 | } |
| 648 | return |
| 649 | } |
| 650 | db.pendingOpens-- |
| 651 | if err != nil { |
| 652 | db.putConnDBLocked(nil, err) |
| 653 | return |
| 654 | } |
| 655 | dc := &driverConn{ |
| 656 | db: db, |
| 657 | ci: ci, |
| 658 | } |
Alberto GarcĂa Hierro | 37db880 | 2013-10-16 09:22:57 -0700 | [diff] [blame] | 659 | if db.putConnDBLocked(dc, err) { |
| 660 | db.addDepLocked(dc, dc) |
| 661 | db.numOpen++ |
| 662 | } else { |
| 663 | ci.Close() |
| 664 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 665 | } |
| 666 | |
| 667 | // connRequest represents one request for a new connection |
| 668 | // When there are no idle connections available, DB.conn will create |
| 669 | // a new connRequest and put it on the db.connRequests list. |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 670 | type connRequest struct { |
| 671 | conn *driverConn |
| 672 | err error |
| 673 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 674 | |
| 675 | var errDBClosed = errors.New("sql: database is closed") |
| 676 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 677 | // conn returns a newly-opened or cached *driverConn. |
| 678 | func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 679 | db.mu.Lock() |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 680 | if db.closed { |
Brad Fitzpatrick | 06a9bc6 | 2011-12-12 13:56:56 -0800 | [diff] [blame] | 681 | db.mu.Unlock() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 682 | return nil, errDBClosed |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 683 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 684 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 685 | // Prefer a free connection, if possible. |
| 686 | numFree := len(db.freeConn) |
| 687 | if strategy == cachedOrNewConn && numFree > 0 { |
| 688 | conn := db.freeConn[0] |
| 689 | copy(db.freeConn, db.freeConn[1:]) |
| 690 | db.freeConn = db.freeConn[:numFree-1] |
| 691 | conn.inUse = true |
| 692 | db.mu.Unlock() |
| 693 | return conn, nil |
| 694 | } |
| 695 | |
| 696 | // Out of free connections or we were asked not to use one. If we're not |
| 697 | // allowed to open any more connections, make a request and wait. |
| 698 | if db.maxOpen > 0 && db.numOpen >= db.maxOpen { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 699 | // Make the connRequest channel. It's buffered so that the |
| 700 | // connectionOpener doesn't block while waiting for the req to be read. |
Brad Fitzpatrick | 558bd8e | 2014-08-28 11:07:29 -0700 | [diff] [blame] | 701 | req := make(chan connRequest, 1) |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 702 | db.connRequests = append(db.connRequests, req) |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 703 | db.mu.Unlock() |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 704 | ret := <-req |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 705 | return ret.conn, ret.err |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 706 | } |
| 707 | |
Brad Fitzpatrick | ce6b75d | 2014-05-07 11:54:29 -0700 | [diff] [blame] | 708 | db.numOpen++ // optimistically |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 709 | db.mu.Unlock() |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 710 | ci, err := db.driver.Open(db.dsn) |
| 711 | if err != nil { |
Brad Fitzpatrick | ce6b75d | 2014-05-07 11:54:29 -0700 | [diff] [blame] | 712 | db.mu.Lock() |
| 713 | db.numOpen-- // correct for earlier optimism |
| 714 | db.mu.Unlock() |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 715 | return nil, err |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 716 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 717 | db.mu.Lock() |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 718 | dc := &driverConn{ |
| 719 | db: db, |
| 720 | ci: ci, |
| 721 | } |
Brad Fitzpatrick | 209f6b1 | 2013-03-25 16:50:27 -0700 | [diff] [blame] | 722 | db.addDepLocked(dc, dc) |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 723 | dc.inUse = true |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 724 | db.mu.Unlock() |
| 725 | return dc, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 726 | } |
| 727 | |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 728 | var ( |
| 729 | errConnClosed = errors.New("database/sql: internal sentinel error: conn is closed") |
| 730 | errConnBusy = errors.New("database/sql: internal sentinel error: conn is busy") |
| 731 | ) |
| 732 | |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 733 | // putConnHook is a hook for testing. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 734 | var putConnHook func(*DB, *driverConn) |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 735 | |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 736 | // noteUnusedDriverStatement notes that si is no longer used and should |
| 737 | // be closed whenever possible (when c is next not in use), unless c is |
| 738 | // already closed. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 739 | func (db *DB) noteUnusedDriverStatement(c *driverConn, si driver.Stmt) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 740 | db.mu.Lock() |
| 741 | defer db.mu.Unlock() |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 742 | if c.inUse { |
| 743 | c.onPut = append(c.onPut, func() { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 744 | si.Close() |
| 745 | }) |
| 746 | } else { |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 747 | c.Lock() |
| 748 | defer c.Unlock() |
| 749 | if !c.finalClosed { |
| 750 | si.Close() |
| 751 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 752 | } |
| 753 | } |
| 754 | |
| 755 | // debugGetPut determines whether getConn & putConn calls' stack traces |
| 756 | // are returned for more verbose crashes. |
| 757 | const debugGetPut = false |
| 758 | |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 759 | // putConn adds a connection to the db's free pool. |
Shenghou Ma | d1ef9b5 | 2012-12-19 03:04:09 +0800 | [diff] [blame] | 760 | // err is optionally the last error that occurred on this connection. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 761 | func (db *DB) putConn(dc *driverConn, err error) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 762 | db.mu.Lock() |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 763 | if !dc.inUse { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 764 | if debugGetPut { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 765 | fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc]) |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 766 | } |
| 767 | panic("sql: connection returned that was never out") |
| 768 | } |
| 769 | if debugGetPut { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 770 | db.lastPut[dc] = stack() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 771 | } |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 772 | dc.inUse = false |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 773 | |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 774 | for _, fn := range dc.onPut { |
| 775 | fn() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 776 | } |
James Tucker | 4f1ef56 | 2013-04-03 11:13:40 -0700 | [diff] [blame] | 777 | dc.onPut = nil |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 778 | |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 779 | if err == driver.ErrBadConn { |
| 780 | // Don't reuse bad connections. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 781 | // Since the conn is considered bad and is being discarded, treat it |
Alberto GarcĂa Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 782 | // as closed. Don't decrement the open count here, finalClose will |
| 783 | // take care of that. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 784 | db.maybeOpenNewConnections() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 785 | db.mu.Unlock() |
Matt Joiner | 13c7896 | 2013-08-14 09:27:30 -0700 | [diff] [blame] | 786 | dc.Close() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 787 | return |
| 788 | } |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 789 | if putConnHook != nil { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 790 | putConnHook(db, dc) |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 791 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 792 | added := db.putConnDBLocked(dc, nil) |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 793 | db.mu.Unlock() |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 794 | |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 795 | if !added { |
| 796 | dc.Close() |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | // Satisfy a connRequest or put the driverConn in the idle pool and return true |
| 801 | // or return false. |
| 802 | // putConnDBLocked will satisfy a connRequest if there is one, or it will |
Marko Tiikkaja | ab05a85 | 2013-12-17 14:53:31 -0800 | [diff] [blame] | 803 | // return the *driverConn to the freeConn list if err == nil and the idle |
| 804 | // connection limit will not be exceeded. |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 805 | // If err != nil, the value of dc is ignored. |
| 806 | // If err == nil, then dc must not equal nil. |
Robert Hencke | f999e14 | 2014-04-29 12:44:40 -0400 | [diff] [blame] | 807 | // If a connRequest was fulfilled or the *driverConn was placed in the |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 808 | // freeConn list, then true is returned, otherwise false is returned. |
| 809 | func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { |
Jiong Du | cce127a | 2014-12-30 16:12:50 +0800 | [diff] [blame] | 810 | if db.maxOpen > 0 && db.numOpen > db.maxOpen { |
| 811 | return false |
| 812 | } |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 813 | if c := len(db.connRequests); c > 0 { |
| 814 | req := db.connRequests[0] |
Brad Fitzpatrick | 558bd8e | 2014-08-28 11:07:29 -0700 | [diff] [blame] | 815 | // This copy is O(n) but in practice faster than a linked list. |
| 816 | // TODO: consider compacting it down less often and |
| 817 | // moving the base instead? |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 818 | copy(db.connRequests, db.connRequests[1:]) |
| 819 | db.connRequests = db.connRequests[:c-1] |
| 820 | if err == nil { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 821 | dc.inUse = true |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 822 | } |
Brad Fitzpatrick | 558bd8e | 2014-08-28 11:07:29 -0700 | [diff] [blame] | 823 | req <- connRequest{ |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 824 | conn: dc, |
| 825 | err: err, |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 826 | } |
| 827 | return true |
Alberto GarcĂa Hierro | 6fb6f4e | 2014-08-28 08:49:56 -0700 | [diff] [blame] | 828 | } else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) { |
| 829 | db.freeConn = append(db.freeConn, dc) |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 830 | return true |
| 831 | } |
| 832 | return false |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 833 | } |
| 834 | |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 835 | // maxBadConnRetries is the number of maximum retries if the driver returns |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 836 | // driver.ErrBadConn to signal a broken connection before forcing a new |
| 837 | // connection to be opened. |
| 838 | const maxBadConnRetries = 2 |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 839 | |
Brad Fitzpatrick | c53fab9 | 2013-02-20 22:15:36 -0800 | [diff] [blame] | 840 | // Prepare creates a prepared statement for later queries or executions. |
| 841 | // Multiple queries or executions may be run concurrently from the |
| 842 | // returned statement. |
Russ Cox | 3c9f60c | 2015-07-14 16:28:28 -0400 | [diff] [blame] | 843 | // The caller must call the statement's Close method |
| 844 | // when the statement is no longer needed. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 845 | func (db *DB) Prepare(query string) (*Stmt, error) { |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 846 | var stmt *Stmt |
| 847 | var err error |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 848 | for i := 0; i < maxBadConnRetries; i++ { |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 849 | stmt, err = db.prepare(query, cachedOrNewConn) |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 850 | if err != driver.ErrBadConn { |
| 851 | break |
| 852 | } |
| 853 | } |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 854 | if err == driver.ErrBadConn { |
| 855 | return db.prepare(query, alwaysNewConn) |
| 856 | } |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 857 | return stmt, err |
| 858 | } |
| 859 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 860 | func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 861 | // TODO: check if db.driver supports an optional |
| 862 | // driver.Preparer interface and call that instead, if so, |
| 863 | // otherwise we make a prepared statement that's bound |
| 864 | // to a connection, and to execute this prepared statement |
| 865 | // we either need to use this connection (if it's free), else |
| 866 | // get a new connection + re-prepare + execute on that one. |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 867 | dc, err := db.conn(strategy) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 868 | if err != nil { |
| 869 | return nil, err |
| 870 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 871 | dc.Lock() |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 872 | si, err := dc.prepareLocked(query) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 873 | dc.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 874 | if err != nil { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 875 | db.putConn(dc, err) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 876 | return nil, err |
| 877 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 878 | stmt := &Stmt{ |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 879 | db: db, |
| 880 | query: query, |
| 881 | css: []connStmt{{dc, si}}, |
| 882 | lastNumClosed: atomic.LoadUint64(&db.numClosed), |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 883 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 884 | db.addDep(stmt, stmt) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 885 | db.putConn(dc, nil) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 886 | return stmt, nil |
| 887 | } |
| 888 | |
| 889 | // Exec executes a query without returning any rows. |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 890 | // The args are for any placeholder parameters in the query. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 891 | func (db *DB) Exec(query string, args ...interface{}) (Result, error) { |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 892 | var res Result |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 893 | var err error |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 894 | for i := 0; i < maxBadConnRetries; i++ { |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 895 | res, err = db.exec(query, args, cachedOrNewConn) |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 896 | if err != driver.ErrBadConn { |
| 897 | break |
| 898 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 899 | } |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 900 | if err == driver.ErrBadConn { |
| 901 | return db.exec(query, args, alwaysNewConn) |
| 902 | } |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 903 | return res, err |
| 904 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 905 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 906 | func (db *DB) exec(query string, args []interface{}, strategy connReuseStrategy) (res Result, err error) { |
| 907 | dc, err := db.conn(strategy) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 908 | if err != nil { |
| 909 | return nil, err |
| 910 | } |
Julien Schmidt | 37b40da | 2012-08-23 19:29:47 -0700 | [diff] [blame] | 911 | defer func() { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 912 | db.putConn(dc, err) |
Julien Schmidt | 37b40da | 2012-08-23 19:29:47 -0700 | [diff] [blame] | 913 | }() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 914 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 915 | if execer, ok := dc.ci.(driver.Execer); ok { |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 916 | dargs, err := driverArgs(nil, args) |
| 917 | if err != nil { |
| 918 | return nil, err |
| 919 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 920 | dc.Lock() |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 921 | resi, err := execer.Exec(query, dargs) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 922 | dc.Unlock() |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 923 | if err != driver.ErrSkip { |
| 924 | if err != nil { |
| 925 | return nil, err |
| 926 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 927 | return driverResult{dc, resi}, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 928 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 929 | } |
| 930 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 931 | dc.Lock() |
| 932 | si, err := dc.ci.Prepare(query) |
| 933 | dc.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 934 | if err != nil { |
| 935 | return nil, err |
| 936 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 937 | defer withLock(dc, func() { si.Close() }) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 938 | return resultFromStatement(driverStmt{dc, si}, args...) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 939 | } |
| 940 | |
| 941 | // Query executes a query that returns rows, typically a SELECT. |
Brad Fitzpatrick | 20130f1 | 2013-01-11 14:46:49 -0800 | [diff] [blame] | 942 | // The args are for any placeholder parameters in the query. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 943 | func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 944 | var rows *Rows |
| 945 | var err error |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 946 | for i := 0; i < maxBadConnRetries; i++ { |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 947 | rows, err = db.query(query, args, cachedOrNewConn) |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 948 | if err != driver.ErrBadConn { |
| 949 | break |
| 950 | } |
| 951 | } |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 952 | if err == driver.ErrBadConn { |
| 953 | return db.query(query, args, alwaysNewConn) |
| 954 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 955 | return rows, err |
| 956 | } |
| 957 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 958 | func (db *DB) query(query string, args []interface{}, strategy connReuseStrategy) (*Rows, error) { |
| 959 | ci, err := db.conn(strategy) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 960 | if err != nil { |
| 961 | return nil, err |
| 962 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 963 | |
Brad Fitzpatrick | 0bbf0ec | 2013-05-14 16:35:31 -0700 | [diff] [blame] | 964 | return db.queryConn(ci, ci.releaseConn, query, args) |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 965 | } |
| 966 | |
| 967 | // queryConn executes a query on the given connection. |
| 968 | // The connection gets released by the releaseConn function. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 969 | func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, args []interface{}) (*Rows, error) { |
| 970 | if queryer, ok := dc.ci.(driver.Queryer); ok { |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 971 | dargs, err := driverArgs(nil, args) |
| 972 | if err != nil { |
| 973 | releaseConn(err) |
| 974 | return nil, err |
| 975 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 976 | dc.Lock() |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 977 | rowsi, err := queryer.Query(query, dargs) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 978 | dc.Unlock() |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 979 | if err != driver.ErrSkip { |
| 980 | if err != nil { |
| 981 | releaseConn(err) |
| 982 | return nil, err |
| 983 | } |
Brad Fitzpatrick | a7a803c | 2013-03-18 11:39:00 -0700 | [diff] [blame] | 984 | // Note: ownership of dc passes to the *Rows, to be freed |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 985 | // with releaseConn. |
| 986 | rows := &Rows{ |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 987 | dc: dc, |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 988 | releaseConn: releaseConn, |
| 989 | rowsi: rowsi, |
| 990 | } |
| 991 | return rows, nil |
| 992 | } |
| 993 | } |
| 994 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 995 | dc.Lock() |
| 996 | si, err := dc.ci.Prepare(query) |
| 997 | dc.Unlock() |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 998 | if err != nil { |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 999 | releaseConn(err) |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 1000 | return nil, err |
| 1001 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1002 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1003 | ds := driverStmt{dc, si} |
| 1004 | rowsi, err := rowsiFromStatement(ds, args...) |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1005 | if err != nil { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1006 | dc.Lock() |
| 1007 | si.Close() |
| 1008 | dc.Unlock() |
Alex Brainman | a293065 | 2013-07-23 14:09:53 +1000 | [diff] [blame] | 1009 | releaseConn(err) |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1010 | return nil, err |
| 1011 | } |
| 1012 | |
| 1013 | // Note: ownership of ci passes to the *Rows, to be freed |
| 1014 | // with releaseConn. |
| 1015 | rows := &Rows{ |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1016 | dc: dc, |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1017 | releaseConn: releaseConn, |
| 1018 | rowsi: rowsi, |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1019 | closeStmt: si, |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1020 | } |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 1021 | return rows, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1022 | } |
| 1023 | |
| 1024 | // QueryRow executes a query that is expected to return at most one row. |
| 1025 | // QueryRow always return a non-nil value. Errors are deferred until |
| 1026 | // Row's Scan method is called. |
| 1027 | func (db *DB) QueryRow(query string, args ...interface{}) *Row { |
| 1028 | rows, err := db.Query(query, args...) |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1029 | return &Row{rows: rows, err: err} |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1030 | } |
| 1031 | |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1032 | // Begin starts a transaction. The isolation level is dependent on |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1033 | // the driver. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1034 | func (db *DB) Begin() (*Tx, error) { |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 1035 | var tx *Tx |
| 1036 | var err error |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1037 | for i := 0; i < maxBadConnRetries; i++ { |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1038 | tx, err = db.begin(cachedOrNewConn) |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 1039 | if err != driver.ErrBadConn { |
| 1040 | break |
| 1041 | } |
| 1042 | } |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1043 | if err == driver.ErrBadConn { |
| 1044 | return db.begin(alwaysNewConn) |
| 1045 | } |
Brad Fitzpatrick | 9fb68a9 | 2012-03-08 10:09:52 -0800 | [diff] [blame] | 1046 | return tx, err |
| 1047 | } |
| 1048 | |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1049 | func (db *DB) begin(strategy connReuseStrategy) (tx *Tx, err error) { |
| 1050 | dc, err := db.conn(strategy) |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1051 | if err != nil { |
| 1052 | return nil, err |
| 1053 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1054 | dc.Lock() |
| 1055 | txi, err := dc.ci.Begin() |
| 1056 | dc.Unlock() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1057 | if err != nil { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1058 | db.putConn(dc, err) |
James David Chalfant | 309eae1 | 2012-12-12 22:04:55 -0800 | [diff] [blame] | 1059 | return nil, err |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1060 | } |
| 1061 | return &Tx{ |
| 1062 | db: db, |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1063 | dc: dc, |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1064 | txi: txi, |
| 1065 | }, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1066 | } |
| 1067 | |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1068 | // Driver returns the database's underlying driver. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1069 | func (db *DB) Driver() driver.Driver { |
| 1070 | return db.driver |
| 1071 | } |
| 1072 | |
| 1073 | // Tx is an in-progress database transaction. |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1074 | // |
| 1075 | // A transaction must end with a call to Commit or Rollback. |
| 1076 | // |
| 1077 | // After a call to Commit or Rollback, all operations on the |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1078 | // transaction fail with ErrTxDone. |
Russ Cox | 3c9f60c | 2015-07-14 16:28:28 -0400 | [diff] [blame] | 1079 | // |
| 1080 | // The statements prepared for a transaction by calling |
| 1081 | // the transaction's Prepare or Stmt methods are closed |
| 1082 | // by the call to Commit or Rollback. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1083 | type Tx struct { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1084 | db *DB |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1085 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1086 | // dc is owned exclusively until Commit or Rollback, at which point |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1087 | // it's returned with putConn. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1088 | dc *driverConn |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1089 | txi driver.Tx |
| 1090 | |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1091 | // done transitions from false to true exactly once, on Commit |
| 1092 | // or Rollback. once done, all operations fail with |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1093 | // ErrTxDone. |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1094 | done bool |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1095 | |
| 1096 | // All Stmts prepared for this transaction. These will be closed after the |
| 1097 | // transaction has been committed or rolled back. |
| 1098 | stmts struct { |
| 1099 | sync.Mutex |
| 1100 | v []*Stmt |
| 1101 | } |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1102 | } |
| 1103 | |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1104 | var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1105 | |
| 1106 | func (tx *Tx) close() { |
| 1107 | if tx.done { |
| 1108 | panic("double close") // internal error |
| 1109 | } |
| 1110 | tx.done = true |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1111 | tx.db.putConn(tx.dc, nil) |
| 1112 | tx.dc = nil |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1113 | tx.txi = nil |
| 1114 | } |
| 1115 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1116 | func (tx *Tx) grabConn() (*driverConn, error) { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1117 | if tx.done { |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1118 | return nil, ErrTxDone |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1119 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1120 | return tx.dc, nil |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1121 | } |
| 1122 | |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1123 | // Closes all Stmts prepared for this transaction. |
| 1124 | func (tx *Tx) closePrepared() { |
| 1125 | tx.stmts.Lock() |
| 1126 | for _, stmt := range tx.stmts.v { |
| 1127 | stmt.Close() |
| 1128 | } |
| 1129 | tx.stmts.Unlock() |
| 1130 | } |
| 1131 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1132 | // Commit commits the transaction. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1133 | func (tx *Tx) Commit() error { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1134 | if tx.done { |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1135 | return ErrTxDone |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1136 | } |
| 1137 | defer tx.close() |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1138 | tx.dc.Lock() |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1139 | err := tx.txi.Commit() |
| 1140 | tx.dc.Unlock() |
| 1141 | if err != driver.ErrBadConn { |
| 1142 | tx.closePrepared() |
| 1143 | } |
| 1144 | return err |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1145 | } |
| 1146 | |
| 1147 | // Rollback aborts the transaction. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1148 | func (tx *Tx) Rollback() error { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1149 | if tx.done { |
Brad Fitzpatrick | 00651a2 | 2012-02-10 09:12:32 +1100 | [diff] [blame] | 1150 | return ErrTxDone |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1151 | } |
| 1152 | defer tx.close() |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1153 | tx.dc.Lock() |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1154 | err := tx.txi.Rollback() |
| 1155 | tx.dc.Unlock() |
| 1156 | if err != driver.ErrBadConn { |
| 1157 | tx.closePrepared() |
| 1158 | } |
| 1159 | return err |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1160 | } |
| 1161 | |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1162 | // Prepare creates a prepared statement for use within a transaction. |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1163 | // |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1164 | // The returned statement operates within the transaction and can no longer |
| 1165 | // be used once the transaction has been committed or rolled back. |
| 1166 | // |
| 1167 | // To use an existing prepared statement on this transaction, see Tx.Stmt. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1168 | func (tx *Tx) Prepare(query string) (*Stmt, error) { |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1169 | // TODO(bradfitz): We could be more efficient here and either |
| 1170 | // provide a method to take an existing Stmt (created on |
| 1171 | // perhaps a different Conn), and re-create it on this Conn if |
| 1172 | // necessary. Or, better: keep a map in DB of query string to |
| 1173 | // Stmts, and have Stmt.Execute do the right thing and |
| 1174 | // re-prepare if the Conn in use doesn't have that prepared |
| 1175 | // statement. But we'll want to avoid caching the statement |
| 1176 | // in the case where we only call conn.Prepare implicitly |
| 1177 | // (such as in db.Exec or tx.Exec), but the caller package |
| 1178 | // can't be holding a reference to the returned statement. |
| 1179 | // Perhaps just looking at the reference count (by noting |
| 1180 | // Stmt.Close) would be enough. We might also want a finalizer |
| 1181 | // on Stmt to drop the reference count. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1182 | dc, err := tx.grabConn() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1183 | if err != nil { |
| 1184 | return nil, err |
| 1185 | } |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1186 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1187 | dc.Lock() |
| 1188 | si, err := dc.ci.Prepare(query) |
| 1189 | dc.Unlock() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1190 | if err != nil { |
| 1191 | return nil, err |
| 1192 | } |
| 1193 | |
| 1194 | stmt := &Stmt{ |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1195 | db: tx.db, |
| 1196 | tx: tx, |
| 1197 | txsi: &driverStmt{ |
| 1198 | Locker: dc, |
| 1199 | si: si, |
| 1200 | }, |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1201 | query: query, |
| 1202 | } |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1203 | tx.stmts.Lock() |
| 1204 | tx.stmts.v = append(tx.stmts.v, stmt) |
| 1205 | tx.stmts.Unlock() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1206 | return stmt, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1207 | } |
| 1208 | |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1209 | // Stmt returns a transaction-specific prepared statement from |
| 1210 | // an existing statement. |
| 1211 | // |
| 1212 | // Example: |
| 1213 | // updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") |
| 1214 | // ... |
| 1215 | // tx, err := db.Begin() |
| 1216 | // ... |
| 1217 | // res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203) |
Russ Cox | 3c9f60c | 2015-07-14 16:28:28 -0400 | [diff] [blame] | 1218 | // |
| 1219 | // The returned statement operates within the transaction and can no longer |
| 1220 | // be used once the transaction has been committed or rolled back. |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1221 | func (tx *Tx) Stmt(stmt *Stmt) *Stmt { |
| 1222 | // TODO(bradfitz): optimize this. Currently this re-prepares |
| 1223 | // each time. This is fine for now to illustrate the API but |
| 1224 | // we should really cache already-prepared statements |
| 1225 | // per-Conn. See also the big comment in Tx.Prepare. |
| 1226 | |
| 1227 | if tx.db != stmt.db { |
| 1228 | return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")} |
| 1229 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1230 | dc, err := tx.grabConn() |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1231 | if err != nil { |
| 1232 | return &Stmt{stickyErr: err} |
| 1233 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1234 | dc.Lock() |
| 1235 | si, err := dc.ci.Prepare(stmt.query) |
| 1236 | dc.Unlock() |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1237 | txs := &Stmt{ |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1238 | db: tx.db, |
| 1239 | tx: tx, |
| 1240 | txsi: &driverStmt{ |
| 1241 | Locker: dc, |
| 1242 | si: si, |
| 1243 | }, |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1244 | query: stmt.query, |
| 1245 | stickyErr: err, |
| 1246 | } |
Marko Tiikkaja | 5f739d9 | 2014-09-22 09:19:27 -0400 | [diff] [blame] | 1247 | tx.stmts.Lock() |
| 1248 | tx.stmts.v = append(tx.stmts.v, txs) |
| 1249 | tx.stmts.Unlock() |
| 1250 | return txs |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1251 | } |
| 1252 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1253 | // Exec executes a query that doesn't return rows. |
| 1254 | // For example: an INSERT and UPDATE. |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1255 | func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1256 | dc, err := tx.grabConn() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1257 | if err != nil { |
| 1258 | return nil, err |
| 1259 | } |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1260 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1261 | if execer, ok := dc.ci.(driver.Execer); ok { |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 1262 | dargs, err := driverArgs(nil, args) |
| 1263 | if err != nil { |
| 1264 | return nil, err |
| 1265 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1266 | dc.Lock() |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 1267 | resi, err := execer.Exec(query, dargs) |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1268 | dc.Unlock() |
Andrew Balholm | aca4a6c | 2012-02-10 09:19:22 +1100 | [diff] [blame] | 1269 | if err == nil { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1270 | return driverResult{dc, resi}, nil |
Andrew Balholm | aca4a6c | 2012-02-10 09:19:22 +1100 | [diff] [blame] | 1271 | } |
| 1272 | if err != driver.ErrSkip { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1273 | return nil, err |
| 1274 | } |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1275 | } |
| 1276 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1277 | dc.Lock() |
| 1278 | si, err := dc.ci.Prepare(query) |
| 1279 | dc.Unlock() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1280 | if err != nil { |
| 1281 | return nil, err |
| 1282 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1283 | defer withLock(dc, func() { si.Close() }) |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 1284 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1285 | return resultFromStatement(driverStmt{dc, si}, args...) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1286 | } |
| 1287 | |
| 1288 | // Query executes a query that returns rows, typically a SELECT. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1289 | func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { |
Brad Fitzpatrick | a7a803c | 2013-03-18 11:39:00 -0700 | [diff] [blame] | 1290 | dc, err := tx.grabConn() |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1291 | if err != nil { |
| 1292 | return nil, err |
| 1293 | } |
Brad Fitzpatrick | a7a803c | 2013-03-18 11:39:00 -0700 | [diff] [blame] | 1294 | releaseConn := func(error) {} |
| 1295 | return tx.db.queryConn(dc, releaseConn, query, args) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1296 | } |
| 1297 | |
| 1298 | // QueryRow executes a query that is expected to return at most one row. |
| 1299 | // QueryRow always return a non-nil value. Errors are deferred until |
| 1300 | // Row's Scan method is called. |
| 1301 | func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1302 | rows, err := tx.Query(query, args...) |
| 1303 | return &Row{rows: rows, err: err} |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1304 | } |
| 1305 | |
| 1306 | // connStmt is a prepared statement on a particular connection. |
| 1307 | type connStmt struct { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1308 | dc *driverConn |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1309 | si driver.Stmt |
| 1310 | } |
| 1311 | |
Russ Cox | 3c9f60c | 2015-07-14 16:28:28 -0400 | [diff] [blame] | 1312 | // Stmt is a prepared statement. |
| 1313 | // A Stmt is safe for concurrent use by multiple goroutines. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1314 | type Stmt struct { |
| 1315 | // Immutable: |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1316 | db *DB // where we came from |
| 1317 | query string // that created the Stmt |
| 1318 | stickyErr error // if non-nil, this error is returned for all operations |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1319 | |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1320 | closemu sync.RWMutex // held exclusively during close, for read otherwise. |
| 1321 | |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1322 | // If in a transaction, else both nil: |
| 1323 | tx *Tx |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1324 | txsi *driverStmt |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1325 | |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1326 | mu sync.Mutex // protects the rest of the fields |
| 1327 | closed bool |
| 1328 | |
| 1329 | // css is a list of underlying driver statement interfaces |
| 1330 | // that are valid on particular connections. This is only |
| 1331 | // used if tx == nil and one is found that has idle |
| 1332 | // connections. If tx != nil, txsi is always used. |
| 1333 | css []connStmt |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 1334 | |
| 1335 | // lastNumClosed is copied from db.numClosed when Stmt is created |
| 1336 | // without tx and closed connections in css are removed. |
| 1337 | lastNumClosed uint64 |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1338 | } |
| 1339 | |
| 1340 | // Exec executes a prepared statement with the given arguments and |
| 1341 | // returns a Result summarizing the effect of the statement. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1342 | func (s *Stmt) Exec(args ...interface{}) (Result, error) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1343 | s.closemu.RLock() |
| 1344 | defer s.closemu.RUnlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1345 | |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1346 | var res Result |
| 1347 | for i := 0; i < maxBadConnRetries; i++ { |
| 1348 | dc, releaseConn, si, err := s.connStmt() |
| 1349 | if err != nil { |
| 1350 | if err == driver.ErrBadConn { |
| 1351 | continue |
| 1352 | } |
| 1353 | return nil, err |
| 1354 | } |
| 1355 | |
| 1356 | res, err = resultFromStatement(driverStmt{dc, si}, args...) |
| 1357 | releaseConn(err) |
| 1358 | if err != driver.ErrBadConn { |
| 1359 | return res, err |
| 1360 | } |
| 1361 | } |
| 1362 | return nil, driver.ErrBadConn |
Gwenael Treguier | 7f0449a | 2013-01-11 13:28:33 -0800 | [diff] [blame] | 1363 | } |
| 1364 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1365 | func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) { |
| 1366 | ds.Lock() |
| 1367 | want := ds.si.NumInput() |
| 1368 | ds.Unlock() |
| 1369 | |
Yasuhiro Matsumoto | 5e5c5c2 | 2011-11-15 16:29:43 -0800 | [diff] [blame] | 1370 | // -1 means the driver doesn't know how to count the number of |
| 1371 | // placeholders, so we won't sanity check input here and instead let the |
| 1372 | // driver deal with errors. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1373 | if want != -1 && len(args) != want { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1374 | return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args)) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1375 | } |
| 1376 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1377 | dargs, err := driverArgs(&ds, args) |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 1378 | if err != nil { |
| 1379 | return nil, err |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1380 | } |
| 1381 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1382 | ds.Lock() |
| 1383 | resi, err := ds.si.Exec(dargs) |
| 1384 | ds.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1385 | if err != nil { |
| 1386 | return nil, err |
| 1387 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1388 | return driverResult{ds.Locker, resi}, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1389 | } |
| 1390 | |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 1391 | // removeClosedStmtLocked removes closed conns in s.css. |
| 1392 | // |
| 1393 | // To avoid lock contention on DB.mu, we do it only when |
| 1394 | // s.db.numClosed - s.lastNum is large enough. |
| 1395 | func (s *Stmt) removeClosedStmtLocked() { |
| 1396 | t := len(s.css)/2 + 1 |
| 1397 | if t > 10 { |
| 1398 | t = 10 |
| 1399 | } |
| 1400 | dbClosed := atomic.LoadUint64(&s.db.numClosed) |
| 1401 | if dbClosed-s.lastNumClosed < uint64(t) { |
| 1402 | return |
| 1403 | } |
| 1404 | |
| 1405 | s.db.mu.Lock() |
| 1406 | for i := 0; i < len(s.css); i++ { |
| 1407 | if s.css[i].dc.dbmuClosed { |
| 1408 | s.css[i] = s.css[len(s.css)-1] |
| 1409 | s.css = s.css[:len(s.css)-1] |
| 1410 | i-- |
| 1411 | } |
| 1412 | } |
| 1413 | s.db.mu.Unlock() |
| 1414 | s.lastNumClosed = dbClosed |
| 1415 | } |
| 1416 | |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1417 | // connStmt returns a free driver connection on which to execute the |
| 1418 | // statement, a function to call to release the connection, and a |
| 1419 | // statement bound to that connection. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1420 | func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error) { |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 1421 | if err = s.stickyErr; err != nil { |
| 1422 | return |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1423 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1424 | s.mu.Lock() |
| 1425 | if s.closed { |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1426 | s.mu.Unlock() |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1427 | err = errors.New("sql: statement is closed") |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1428 | return |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1429 | } |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1430 | |
| 1431 | // In a transaction, we always use the connection that the |
| 1432 | // transaction was created on. |
| 1433 | if s.tx != nil { |
| 1434 | s.mu.Unlock() |
| 1435 | ci, err = s.tx.grabConn() // blocks, waiting for the connection. |
| 1436 | if err != nil { |
| 1437 | return |
| 1438 | } |
Brad Fitzpatrick | a7a803c | 2013-03-18 11:39:00 -0700 | [diff] [blame] | 1439 | releaseConn = func(error) {} |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1440 | return ci, releaseConn, s.txsi.si, nil |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1441 | } |
| 1442 | |
INADA Naoki | 1b61a97 | 2015-01-23 20:02:37 +0900 | [diff] [blame] | 1443 | s.removeClosedStmtLocked() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1444 | s.mu.Unlock() |
| 1445 | |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1446 | // TODO(bradfitz): or always wait for one? make configurable later? |
Marko Tiikkaja | c468f946 | 2015-03-27 19:45:12 +0100 | [diff] [blame] | 1447 | dc, err := s.db.conn(cachedOrNewConn) |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1448 | if err != nil { |
| 1449 | return nil, nil, nil, err |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1450 | } |
| 1451 | |
Marko Tiikkaja | 90e2e2b | 2014-09-02 09:08:41 -0700 | [diff] [blame] | 1452 | s.mu.Lock() |
| 1453 | for _, v := range s.css { |
| 1454 | if v.dc == dc { |
| 1455 | s.mu.Unlock() |
| 1456 | return dc, dc.releaseConn, v.si, nil |
| 1457 | } |
| 1458 | } |
| 1459 | s.mu.Unlock() |
| 1460 | |
| 1461 | // No luck; we need to prepare the statement on this connection |
| 1462 | dc.Lock() |
| 1463 | si, err = dc.prepareLocked(s.query) |
| 1464 | dc.Unlock() |
| 1465 | if err != nil { |
| 1466 | s.db.putConn(dc, err) |
| 1467 | return nil, nil, nil, err |
| 1468 | } |
| 1469 | s.mu.Lock() |
| 1470 | cs := connStmt{dc, si} |
| 1471 | s.css = append(s.css, cs) |
| 1472 | s.mu.Unlock() |
| 1473 | |
| 1474 | return dc, dc.releaseConn, si, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1475 | } |
| 1476 | |
| 1477 | // Query executes a prepared query statement with the given arguments |
| 1478 | // and returns the query results as a *Rows. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1479 | func (s *Stmt) Query(args ...interface{}) (*Rows, error) { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1480 | s.closemu.RLock() |
| 1481 | defer s.closemu.RUnlock() |
| 1482 | |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1483 | var rowsi driver.Rows |
| 1484 | for i := 0; i < maxBadConnRetries; i++ { |
| 1485 | dc, releaseConn, si, err := s.connStmt() |
| 1486 | if err != nil { |
| 1487 | if err == driver.ErrBadConn { |
| 1488 | continue |
| 1489 | } |
| 1490 | return nil, err |
| 1491 | } |
Yasuhiro Matsumoto | 5e5c5c2 | 2011-11-15 16:29:43 -0800 | [diff] [blame] | 1492 | |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1493 | rowsi, err = rowsiFromStatement(driverStmt{dc, si}, args...) |
| 1494 | if err == nil { |
| 1495 | // Note: ownership of ci passes to the *Rows, to be freed |
| 1496 | // with releaseConn. |
| 1497 | rows := &Rows{ |
| 1498 | dc: dc, |
| 1499 | rowsi: rowsi, |
| 1500 | // releaseConn set below |
| 1501 | } |
| 1502 | s.db.addDep(s, rows) |
| 1503 | rows.releaseConn = func(err error) { |
| 1504 | releaseConn(err) |
| 1505 | s.db.removeDep(s, rows) |
| 1506 | } |
| 1507 | return rows, nil |
| 1508 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1509 | |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1510 | releaseConn(err) |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1511 | if err != driver.ErrBadConn { |
| 1512 | return nil, err |
| 1513 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1514 | } |
Julien Schmidt | 762a9d9 | 2013-12-17 11:57:30 -0800 | [diff] [blame] | 1515 | return nil, driver.ErrBadConn |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1516 | } |
| 1517 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1518 | func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error) { |
| 1519 | ds.Lock() |
| 1520 | want := ds.si.NumInput() |
| 1521 | ds.Unlock() |
| 1522 | |
Yasuhiro Matsumoto | 5e5c5c2 | 2011-11-15 16:29:43 -0800 | [diff] [blame] | 1523 | // -1 means the driver doesn't know how to count the number of |
| 1524 | // placeholders, so we won't sanity check input here and instead let the |
| 1525 | // driver deal with errors. |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1526 | if want != -1 && len(args) != want { |
| 1527 | return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(args)) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1528 | } |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 1529 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1530 | dargs, err := driverArgs(&ds, args) |
Brad Fitzpatrick | 0a8005c | 2011-11-14 10:48:26 -0800 | [diff] [blame] | 1531 | if err != nil { |
| 1532 | return nil, err |
| 1533 | } |
Brad Fitzpatrick | 93fe8c0c | 2012-05-29 11:09:09 -0700 | [diff] [blame] | 1534 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1535 | ds.Lock() |
| 1536 | rowsi, err := ds.si.Query(dargs) |
| 1537 | ds.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1538 | if err != nil { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1539 | return nil, err |
| 1540 | } |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1541 | return rowsi, nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1542 | } |
| 1543 | |
| 1544 | // QueryRow executes a prepared query statement with the given arguments. |
| 1545 | // If an error occurs during the execution of the statement, that error will |
| 1546 | // be returned by a call to Scan on the returned *Row, which is always non-nil. |
| 1547 | // If the query selects no rows, the *Row's Scan will return ErrNoRows. |
| 1548 | // Otherwise, the *Row's Scan scans the first selected row and discards |
| 1549 | // the rest. |
| 1550 | // |
| 1551 | // Example usage: |
| 1552 | // |
| 1553 | // var name string |
Brad Fitzpatrick | 6bdd791 | 2012-02-10 10:20:49 +1100 | [diff] [blame] | 1554 | // err := nameByUseridStmt.QueryRow(id).Scan(&name) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1555 | func (s *Stmt) QueryRow(args ...interface{}) *Row { |
| 1556 | rows, err := s.Query(args...) |
| 1557 | if err != nil { |
| 1558 | return &Row{err: err} |
| 1559 | } |
| 1560 | return &Row{rows: rows} |
| 1561 | } |
| 1562 | |
| 1563 | // Close closes the statement. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1564 | func (s *Stmt) Close() error { |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1565 | s.closemu.Lock() |
| 1566 | defer s.closemu.Unlock() |
| 1567 | |
Brad Fitzpatrick | e77099d | 2011-11-28 11:00:32 -0500 | [diff] [blame] | 1568 | if s.stickyErr != nil { |
| 1569 | return s.stickyErr |
| 1570 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1571 | s.mu.Lock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1572 | if s.closed { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1573 | s.mu.Unlock() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1574 | return nil |
| 1575 | } |
| 1576 | s.closed = true |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1577 | |
| 1578 | if s.tx != nil { |
| 1579 | s.txsi.Close() |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1580 | s.mu.Unlock() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1581 | return nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1582 | } |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1583 | s.mu.Unlock() |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1584 | |
| 1585 | return s.db.removeDep(s, s) |
| 1586 | } |
| 1587 | |
| 1588 | func (s *Stmt) finalClose() error { |
Tad Glines | 41c5d8d | 2013-08-30 09:27:33 -0700 | [diff] [blame] | 1589 | s.mu.Lock() |
| 1590 | defer s.mu.Unlock() |
| 1591 | if s.css != nil { |
| 1592 | for _, v := range s.css { |
| 1593 | s.db.noteUnusedDriverStatement(v.dc, v.si) |
| 1594 | v.dc.removeOpenStmt(v.si) |
| 1595 | } |
| 1596 | s.css = nil |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1597 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1598 | return nil |
| 1599 | } |
| 1600 | |
| 1601 | // Rows is the result of a query. Its cursor starts before the first row |
| 1602 | // of the result set. Use Next to advance through the rows: |
| 1603 | // |
| 1604 | // rows, err := db.Query("SELECT ...") |
| 1605 | // ... |
Nigel Tao | 50ca1a5 | 2014-03-25 13:32:18 +1100 | [diff] [blame] | 1606 | // defer rows.Close() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1607 | // for rows.Next() { |
| 1608 | // var id int |
| 1609 | // var name string |
| 1610 | // err = rows.Scan(&id, &name) |
| 1611 | // ... |
| 1612 | // } |
Gustavo Niemeyer | f2dc50b | 2011-11-04 09:50:20 -0400 | [diff] [blame] | 1613 | // err = rows.Err() // get any error encountered during iteration |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1614 | // ... |
| 1615 | type Rows struct { |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1616 | dc *driverConn // owned; must call releaseConn when closed to release |
Brad Fitzpatrick | 3297fc6 | 2012-03-10 10:00:02 -0800 | [diff] [blame] | 1617 | releaseConn func(error) |
Brad Fitzpatrick | 8089e57 | 2011-11-02 11:46:04 -0700 | [diff] [blame] | 1618 | rowsi driver.Rows |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1619 | |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 1620 | closed bool |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 1621 | lastcols []driver.Value |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1622 | lasterr error // non-nil only if closed is true |
Julien Schmidt | 2968e23 | 2013-02-13 15:25:39 -0800 | [diff] [blame] | 1623 | closeStmt driver.Stmt // if non-nil, statement to Close on close |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1624 | } |
| 1625 | |
Marko Tiikkaja | 1f20ab1 | 2013-12-16 12:48:35 -0800 | [diff] [blame] | 1626 | // Next prepares the next result row for reading with the Scan method. It |
| 1627 | // returns true on success, or false if there is no next result row or an error |
| 1628 | // happened while preparing it. Err should be consulted to distinguish between |
| 1629 | // the two cases. |
| 1630 | // |
| 1631 | // Every call to Scan, even the first one, must be preceded by a call to Next. |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1632 | func (rs *Rows) Next() bool { |
| 1633 | if rs.closed { |
| 1634 | return false |
| 1635 | } |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1636 | if rs.lastcols == nil { |
Brad Fitzpatrick | 943f6cc | 2012-02-20 14:25:28 +1100 | [diff] [blame] | 1637 | rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1638 | } |
| 1639 | rs.lasterr = rs.rowsi.Next(rs.lastcols) |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1640 | if rs.lasterr != nil { |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 1641 | rs.Close() |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1642 | return false |
Brad Fitzpatrick | 4435c8b | 2012-01-10 12:51:27 -0800 | [diff] [blame] | 1643 | } |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1644 | return true |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1645 | } |
| 1646 | |
Gustavo Niemeyer | f2dc50b | 2011-11-04 09:50:20 -0400 | [diff] [blame] | 1647 | // Err returns the error, if any, that was encountered during iteration. |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1648 | // Err may be called after an explicit or implicit Close. |
Gustavo Niemeyer | f2dc50b | 2011-11-04 09:50:20 -0400 | [diff] [blame] | 1649 | func (rs *Rows) Err() error { |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1650 | if rs.lasterr == io.EOF { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1651 | return nil |
| 1652 | } |
| 1653 | return rs.lasterr |
| 1654 | } |
| 1655 | |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1656 | // Columns returns the column names. |
| 1657 | // Columns returns an error if the rows are closed, or if the rows |
| 1658 | // are from QueryRow and there was a deferred error. |
| 1659 | func (rs *Rows) Columns() ([]string, error) { |
| 1660 | if rs.closed { |
| 1661 | return nil, errors.New("sql: Rows are closed") |
| 1662 | } |
| 1663 | if rs.rowsi == nil { |
| 1664 | return nil, errors.New("sql: no Rows available") |
| 1665 | } |
| 1666 | return rs.rowsi.Columns(), nil |
| 1667 | } |
| 1668 | |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1669 | // Scan copies the columns in the current row into the values pointed |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 1670 | // at by dest. |
| 1671 | // |
| 1672 | // If an argument has type *[]byte, Scan saves in that argument a copy |
| 1673 | // of the corresponding data. The copy is owned by the caller and can |
| 1674 | // be modified and held indefinitely. The copy can be avoided by using |
| 1675 | // an argument of type *RawBytes instead; see the documentation for |
| 1676 | // RawBytes for restrictions on its use. |
Brad Fitzpatrick | 9c060b8 | 2012-02-06 10:06:22 -0800 | [diff] [blame] | 1677 | // |
| 1678 | // If an argument has type *interface{}, Scan copies the value |
| 1679 | // provided by the underlying driver without conversion. If the value |
| 1680 | // is of type []byte, a copy is made and the caller owns the result. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1681 | func (rs *Rows) Scan(dest ...interface{}) error { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1682 | if rs.closed { |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1683 | return errors.New("sql: Rows are closed") |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1684 | } |
| 1685 | if rs.lastcols == nil { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1686 | return errors.New("sql: Scan called without calling Next") |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1687 | } |
| 1688 | if len(dest) != len(rs.lastcols) { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1689 | return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1690 | } |
| 1691 | for i, sv := range rs.lastcols { |
| 1692 | err := convertAssign(dest[i], sv) |
| 1693 | if err != nil { |
Brad Fitzpatrick | ea51dd2 | 2011-12-15 10:14:57 -0800 | [diff] [blame] | 1694 | return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1695 | } |
| 1696 | } |
| 1697 | return nil |
| 1698 | } |
| 1699 | |
Brad Fitzpatrick | ca3ed9f | 2013-08-13 14:56:40 -0700 | [diff] [blame] | 1700 | var rowsCloseHook func(*Rows, *error) |
| 1701 | |
Nigel Tao | bc21265 | 2013-08-16 11:23:35 +1000 | [diff] [blame] | 1702 | // Close closes the Rows, preventing further enumeration. If Next returns |
| 1703 | // false, the Rows are closed automatically and it will suffice to check the |
| 1704 | // result of Err. Close is idempotent and does not affect the result of Err. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1705 | func (rs *Rows) Close() error { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1706 | if rs.closed { |
| 1707 | return nil |
| 1708 | } |
| 1709 | rs.closed = true |
| 1710 | err := rs.rowsi.Close() |
Brad Fitzpatrick | ca3ed9f | 2013-08-13 14:56:40 -0700 | [diff] [blame] | 1711 | if fn := rowsCloseHook; fn != nil { |
| 1712 | fn(rs, &err) |
| 1713 | } |
Brad Fitzpatrick | 1c441e2 | 2012-01-13 15:25:07 -0800 | [diff] [blame] | 1714 | if rs.closeStmt != nil { |
| 1715 | rs.closeStmt.Close() |
| 1716 | } |
Brad Fitzpatrick | 36d3bef | 2013-04-15 14:06:41 -0700 | [diff] [blame] | 1717 | rs.releaseConn(err) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1718 | return err |
| 1719 | } |
| 1720 | |
| 1721 | // Row is the result of calling QueryRow to select a single row. |
| 1722 | type Row struct { |
| 1723 | // One of these two will be non-nil: |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1724 | err error // deferred error for easy chaining |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1725 | rows *Rows |
| 1726 | } |
| 1727 | |
| 1728 | // Scan copies the columns from the matched row into the values |
| 1729 | // pointed at by dest. If more than one row matches the query, |
| 1730 | // Scan uses the first row and discards the rest. If no row matches |
| 1731 | // the query, Scan returns ErrNoRows. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1732 | func (r *Row) Scan(dest ...interface{}) error { |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1733 | if r.err != nil { |
| 1734 | return r.err |
| 1735 | } |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 1736 | |
| 1737 | // TODO(bradfitz): for now we need to defensively clone all |
Gwenael Treguier | c3954dd5 | 2012-03-10 15:21:44 -0800 | [diff] [blame] | 1738 | // []byte that the driver returned (not permitting |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 1739 | // *RawBytes in Rows.Scan), since we're about to close |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 1740 | // the Rows in our defer, when we return from this function. |
| 1741 | // the contract with the driver.Next(...) interface is that it |
| 1742 | // can return slices into read-only temporary memory that's |
| 1743 | // only valid until the next Scan/Close. But the TODO is that |
| 1744 | // for a lot of drivers, this copy will be unnecessary. We |
| 1745 | // should provide an optional interface for drivers to |
| 1746 | // implement to say, "don't worry, the []bytes that I return |
| 1747 | // from Next will not be modified again." (for instance, if |
| 1748 | // they were obtained from the network anyway) But for now we |
| 1749 | // don't care. |
Alberto GarcĂa Hierro | 478f4b6 | 2013-10-16 09:17:25 -0700 | [diff] [blame] | 1750 | defer r.rows.Close() |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 1751 | for _, dp := range dest { |
Brad Fitzpatrick | ebc8013 | 2012-01-17 10:44:35 -0800 | [diff] [blame] | 1752 | if _, ok := dp.(*RawBytes); ok { |
| 1753 | return errors.New("sql: RawBytes isn't allowed on Row.Scan") |
| 1754 | } |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 1755 | } |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 1756 | |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 1757 | if !r.rows.Next() { |
Marko Tiikkaja | 1f20ab1 | 2013-12-16 12:48:35 -0800 | [diff] [blame] | 1758 | if err := r.rows.Err(); err != nil { |
| 1759 | return err |
| 1760 | } |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 1761 | return ErrNoRows |
| 1762 | } |
| 1763 | err := r.rows.Scan(dest...) |
| 1764 | if err != nil { |
| 1765 | return err |
| 1766 | } |
Marko Tiikkaja | 1f20ab1 | 2013-12-16 12:48:35 -0800 | [diff] [blame] | 1767 | // Make sure the query can be processed to completion with no errors. |
| 1768 | if err := r.rows.Close(); err != nil { |
| 1769 | return err |
| 1770 | } |
James P. Cooper | 2a22f35 | 2012-01-26 15:12:48 -0800 | [diff] [blame] | 1771 | |
Brad Fitzpatrick | 701f70a | 2012-01-12 11:23:33 -0800 | [diff] [blame] | 1772 | return nil |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1773 | } |
| 1774 | |
| 1775 | // A Result summarizes an executed SQL command. |
| 1776 | type Result interface { |
Brad Fitzpatrick | 7307ffa | 2013-10-29 17:38:43 -0700 | [diff] [blame] | 1777 | // LastInsertId returns the integer generated by the database |
| 1778 | // in response to a command. Typically this will be from an |
| 1779 | // "auto increment" column when inserting a new row. Not all |
| 1780 | // databases support this feature, and the syntax of such |
| 1781 | // statements varies. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1782 | LastInsertId() (int64, error) |
Brad Fitzpatrick | 7307ffa | 2013-10-29 17:38:43 -0700 | [diff] [blame] | 1783 | |
| 1784 | // RowsAffected returns the number of rows affected by an |
| 1785 | // update, insert, or delete. Not every database or database |
| 1786 | // driver may support this. |
Russ Cox | c2049d2 | 2011-11-01 22:04:37 -0400 | [diff] [blame] | 1787 | RowsAffected() (int64, error) |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1788 | } |
| 1789 | |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1790 | type driverResult struct { |
| 1791 | sync.Locker // the *driverConn |
| 1792 | resi driver.Result |
| 1793 | } |
| 1794 | |
| 1795 | func (dr driverResult) LastInsertId() (int64, error) { |
| 1796 | dr.Lock() |
| 1797 | defer dr.Unlock() |
| 1798 | return dr.resi.LastInsertId() |
| 1799 | } |
| 1800 | |
| 1801 | func (dr driverResult) RowsAffected() (int64, error) { |
| 1802 | dr.Lock() |
| 1803 | defer dr.Unlock() |
| 1804 | return dr.resi.RowsAffected() |
Brad Fitzpatrick | 357f2cb | 2011-09-29 16:12:21 -0700 | [diff] [blame] | 1805 | } |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1806 | |
| 1807 | func stack() string { |
Brad Fitzpatrick | 277047f | 2013-04-25 14:45:56 -0700 | [diff] [blame] | 1808 | var buf [2 << 10]byte |
Brad Fitzpatrick | f7a7716 | 2013-02-20 15:35:27 -0800 | [diff] [blame] | 1809 | return string(buf[:runtime.Stack(buf[:], false)]) |
| 1810 | } |
Brad Fitzpatrick | f28c8fb | 2013-03-14 15:01:45 -0700 | [diff] [blame] | 1811 | |
| 1812 | // withLock runs while holding lk. |
| 1813 | func withLock(lk sync.Locker, fn func()) { |
| 1814 | lk.Lock() |
| 1815 | fn() |
| 1816 | lk.Unlock() |
| 1817 | } |