Commit 478f4b67 authored by Alberto García Hierro's avatar Alberto García Hierro Committed by Brad Fitzpatrick

database/sql: fix double decrement of numOpen count; test for connection leaks

Add a check at the end of every test to make sure
there are no leaked connections after running a test.

Avoid incorrectly decrementing the number of open connections
when the driver connection ends up it a bad state (numOpen was
decremented twice).

Prevent leaking a Rows struct (which ends up leaking a
connection) in Row.Scan() when a *RawBytes destination is
improperly used.

Close the Rows struct in TestRowsColumns.

Update #6593

R=golang-dev, bradfitz, dave
CC=golang-dev
https://golang.org/cl/14642044
parent 4d38d126
...@@ -504,7 +504,6 @@ func (db *DB) maxIdleConnsLocked() int { ...@@ -504,7 +504,6 @@ func (db *DB) maxIdleConnsLocked() int {
// If n <= 0, no idle connections are retained. // If n <= 0, no idle connections are retained.
func (db *DB) SetMaxIdleConns(n int) { func (db *DB) SetMaxIdleConns(n int) {
db.mu.Lock() db.mu.Lock()
defer db.mu.Unlock()
if n > 0 { if n > 0 {
db.maxIdle = n db.maxIdle = n
} else { } else {
...@@ -515,11 +514,16 @@ func (db *DB) SetMaxIdleConns(n int) { ...@@ -515,11 +514,16 @@ func (db *DB) SetMaxIdleConns(n int) {
if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen { if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen {
db.maxIdle = db.maxOpen db.maxIdle = db.maxOpen
} }
var closing []*driverConn
for db.freeConn.Len() > db.maxIdleConnsLocked() { for db.freeConn.Len() > db.maxIdleConnsLocked() {
dc := db.freeConn.Back().Value.(*driverConn) dc := db.freeConn.Back().Value.(*driverConn)
dc.listElem = nil dc.listElem = nil
db.freeConn.Remove(db.freeConn.Back()) db.freeConn.Remove(db.freeConn.Back())
go dc.Close() closing = append(closing, dc)
}
db.mu.Unlock()
for _, c := range closing {
c.Close()
} }
} }
...@@ -743,8 +747,8 @@ func (db *DB) putConn(dc *driverConn, err error) { ...@@ -743,8 +747,8 @@ func (db *DB) putConn(dc *driverConn, err error) {
if err == driver.ErrBadConn { if err == driver.ErrBadConn {
// Don't reuse bad connections. // Don't reuse bad connections.
// Since the conn is considered bad and is being discarded, treat it // Since the conn is considered bad and is being discarded, treat it
// as closed. Decrement the open count. // as closed. Don't decrement the open count here, finalClose will
db.numOpen-- // take care of that.
db.maybeOpenNewConnections() db.maybeOpenNewConnections()
db.mu.Unlock() db.mu.Unlock()
dc.Close() dc.Close()
...@@ -1607,13 +1611,13 @@ func (r *Row) Scan(dest ...interface{}) error { ...@@ -1607,13 +1611,13 @@ func (r *Row) Scan(dest ...interface{}) error {
// from Next will not be modified again." (for instance, if // from Next will not be modified again." (for instance, if
// they were obtained from the network anyway) But for now we // they were obtained from the network anyway) But for now we
// don't care. // don't care.
defer r.rows.Close()
for _, dp := range dest { for _, dp := range dest {
if _, ok := dp.(*RawBytes); ok { if _, ok := dp.(*RawBytes); ok {
return errors.New("sql: RawBytes isn't allowed on Row.Scan") return errors.New("sql: RawBytes isn't allowed on Row.Scan")
} }
} }
defer r.rows.Close()
if !r.rows.Next() { if !r.rows.Next() {
return ErrNoRows return ErrNoRows
} }
......
...@@ -94,6 +94,12 @@ func closeDB(t testing.TB, db *DB) { ...@@ -94,6 +94,12 @@ func closeDB(t testing.TB, db *DB) {
if err != nil { if err != nil {
t.Fatalf("error closing DB: %v", err) t.Fatalf("error closing DB: %v", err)
} }
db.mu.Lock()
count := db.numOpen
db.mu.Unlock()
if count != 0 {
t.Fatalf("%d connections still open after closing DB", db.numOpen)
}
} }
// numPrepares assumes that db has exactly 1 idle conn and returns // numPrepares assumes that db has exactly 1 idle conn and returns
...@@ -246,6 +252,9 @@ func TestRowsColumns(t *testing.T) { ...@@ -246,6 +252,9 @@ func TestRowsColumns(t *testing.T) {
if !reflect.DeepEqual(cols, want) { if !reflect.DeepEqual(cols, want) {
t.Errorf("got %#v; want %#v", cols, want) t.Errorf("got %#v; want %#v", cols, want)
} }
if err := rows.Close(); err != nil {
t.Errorf("error closing rows: %s", err)
}
} }
func TestQueryRow(t *testing.T) { func TestQueryRow(t *testing.T) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment