mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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
This commit is contained in:
parent
4d38d1260e
commit
478f4b6754
2 changed files with 18 additions and 5 deletions
|
|
@ -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) {
|
||||||
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) {
|
||||||
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 {
|
||||||
// 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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue