Commit dec95d8f authored by Daniel Theophanes's avatar Daniel Theophanes

database/sql: correctly guard the query Row preventing early release

When a Tx starts a query, prevent returning the connection to the pool
until after the query finishes.

Fixes #19058

Change-Id: I2c0480d9cca9eeb173b5b3441a5aeed6f527e0ac
Reviewed-on: https://go-review.googlesource.com/40400Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 10a200e5
...@@ -1504,6 +1504,14 @@ func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) { ...@@ -1504,6 +1504,14 @@ func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
return tx.dc, nil return tx.dc, nil
} }
// closemuRUnlockRelease is used as a func(error) method value in
// ExecContext and QueryContext. Unlocking in the releaseConn keeps
// the driver conn from being returned to the connection pool until
// the Rows has been closed.
func (tx *Tx) closemuRUnlockRelease(error) {
tx.closemu.RUnlock()
}
// Closes all Stmts prepared for this transaction. // Closes all Stmts prepared for this transaction.
func (tx *Tx) closePrepared() { func (tx *Tx) closePrepared() {
tx.stmts.Lock() tx.stmts.Lock()
...@@ -1713,13 +1721,13 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { ...@@ -1713,13 +1721,13 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
// For example: an INSERT and UPDATE. // For example: an INSERT and UPDATE.
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) { func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
tx.closemu.RLock() tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx) dc, err := tx.grabConn(ctx)
if err != nil { if err != nil {
tx.closemu.RUnlock()
return nil, err return nil, err
} }
return tx.db.execDC(ctx, dc, func(error) {}, query, args) return tx.db.execDC(ctx, dc, tx.closemuRUnlockRelease, query, args)
} }
// Exec executes a query that doesn't return rows. // Exec executes a query that doesn't return rows.
...@@ -1731,14 +1739,14 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { ...@@ -1731,14 +1739,14 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
// QueryContext executes a query that returns rows, typically a SELECT. // QueryContext executes a query that returns rows, typically a SELECT.
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
tx.closemu.RLock() tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx) dc, err := tx.grabConn(ctx)
if err != nil { if err != nil {
tx.closemu.RUnlock()
return nil, err return nil, err
} }
releaseConn := func(error) {}
return tx.db.queryDC(ctx, dc, releaseConn, query, args) return tx.db.queryDC(ctx, dc, tx.closemuRUnlockRelease, query, args)
} }
// Query executes a query that returns rows, typically a SELECT. // Query executes a query that returns rows, typically a SELECT.
......
...@@ -2997,7 +2997,6 @@ func TestIssue18719(t *testing.T) { ...@@ -2997,7 +2997,6 @@ func TestIssue18719(t *testing.T) {
// canceled context. // canceled context.
cancel() cancel()
waitForRowsClose(t, rows, 5*time.Second)
} }
func TestConcurrency(t *testing.T) { func TestConcurrency(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