mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
database/sql: allow simultaneous queries, etc in a Tx
Now that revision 0c029965805f is in, it's easy to guarantee that we never access a driver.Conn concurrently, per the database/sql/driver contract, so we can remove this overlarge mutex. Fixes #3857 R=golang-dev, adg CC=golang-dev https://golang.org/cl/7707047
This commit is contained in:
parent
9db0583007
commit
a7a803c7b7
2 changed files with 30 additions and 21 deletions
|
|
@ -590,7 +590,7 @@ func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, a
|
||||||
releaseConn(err)
|
releaseConn(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Note: ownership of ci passes to the *Rows, to be freed
|
// Note: ownership of dc passes to the *Rows, to be freed
|
||||||
// with releaseConn.
|
// with releaseConn.
|
||||||
rows := &Rows{
|
rows := &Rows{
|
||||||
db: db,
|
db: db,
|
||||||
|
|
@ -689,15 +689,9 @@ type Tx struct {
|
||||||
|
|
||||||
// dc is owned exclusively until Commit or Rollback, at which point
|
// dc is owned exclusively until Commit or Rollback, at which point
|
||||||
// it's returned with putConn.
|
// it's returned with putConn.
|
||||||
// TODO(bradfitz): golang.org/issue/3857
|
|
||||||
dc *driverConn
|
dc *driverConn
|
||||||
txi driver.Tx
|
txi driver.Tx
|
||||||
|
|
||||||
// cimu is held while somebody is using ci (between grabConn
|
|
||||||
// and releaseConn)
|
|
||||||
// TODO(bradfitz): golang.org/issue/3857
|
|
||||||
cimu sync.Mutex
|
|
||||||
|
|
||||||
// done transitions from false to true exactly once, on Commit
|
// done transitions from false to true exactly once, on Commit
|
||||||
// or Rollback. once done, all operations fail with
|
// or Rollback. once done, all operations fail with
|
||||||
// ErrTxDone.
|
// ErrTxDone.
|
||||||
|
|
@ -720,14 +714,9 @@ func (tx *Tx) grabConn() (*driverConn, error) {
|
||||||
if tx.done {
|
if tx.done {
|
||||||
return nil, ErrTxDone
|
return nil, ErrTxDone
|
||||||
}
|
}
|
||||||
tx.cimu.Lock()
|
|
||||||
return tx.dc, nil
|
return tx.dc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Tx) releaseConn() {
|
|
||||||
tx.cimu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit commits the transaction.
|
// Commit commits the transaction.
|
||||||
func (tx *Tx) Commit() error {
|
func (tx *Tx) Commit() error {
|
||||||
if tx.done {
|
if tx.done {
|
||||||
|
|
@ -774,7 +763,6 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer tx.releaseConn()
|
|
||||||
|
|
||||||
dc.Lock()
|
dc.Lock()
|
||||||
si, err := dc.ci.Prepare(query)
|
si, err := dc.ci.Prepare(query)
|
||||||
|
|
@ -817,7 +805,6 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &Stmt{stickyErr: err}
|
return &Stmt{stickyErr: err}
|
||||||
}
|
}
|
||||||
defer tx.releaseConn()
|
|
||||||
dc.Lock()
|
dc.Lock()
|
||||||
si, err := dc.ci.Prepare(stmt.query)
|
si, err := dc.ci.Prepare(stmt.query)
|
||||||
dc.Unlock()
|
dc.Unlock()
|
||||||
|
|
@ -840,7 +827,6 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer tx.releaseConn()
|
|
||||||
|
|
||||||
if execer, ok := dc.ci.(driver.Execer); ok {
|
if execer, ok := dc.ci.(driver.Execer); ok {
|
||||||
dargs, err := driverArgs(nil, args)
|
dargs, err := driverArgs(nil, args)
|
||||||
|
|
@ -871,14 +857,12 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
|
||||||
|
|
||||||
// Query executes a query that returns rows, typically a SELECT.
|
// Query executes a query that returns rows, typically a SELECT.
|
||||||
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
|
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
|
||||||
ci, err := tx.grabConn()
|
dc, err := tx.grabConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
releaseConn := func(error) {}
|
||||||
releaseConn := func(err error) { tx.releaseConn() }
|
return tx.db.queryConn(dc, releaseConn, query, args)
|
||||||
|
|
||||||
return tx.db.queryConn(ci, releaseConn, query, args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRow executes a query that is expected to return at most one row.
|
// QueryRow executes a query that is expected to return at most one row.
|
||||||
|
|
@ -980,7 +964,7 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
releaseConn = func(error) { s.tx.releaseConn() }
|
releaseConn = func(error) {}
|
||||||
return ci, releaseConn, s.txsi.si, nil
|
return ci, releaseConn, s.txsi.si, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -736,3 +736,28 @@ func TestIssue4902(t *testing.T) {
|
||||||
t.Logf("stmt = %#v", stmt)
|
t.Logf("stmt = %#v", stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 3857
|
||||||
|
// This used to deadlock.
|
||||||
|
func TestSimultaneousQueries(t *testing.T) {
|
||||||
|
db := newTestDB(t, "people")
|
||||||
|
defer closeDB(t, db)
|
||||||
|
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
r1, err := tx.Query("SELECT|people|name|")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer r1.Close()
|
||||||
|
|
||||||
|
r2, err := tx.Query("SELECT|people|name|")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer r2.Close()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue