diff options
Diffstat (limited to 'llgo/third_party/gofrontend/libgo/go/database/sql/sql.go')
-rw-r--r-- | llgo/third_party/gofrontend/libgo/go/database/sql/sql.go | 220 |
1 files changed, 135 insertions, 85 deletions
diff --git a/llgo/third_party/gofrontend/libgo/go/database/sql/sql.go b/llgo/third_party/gofrontend/libgo/go/database/sql/sql.go index 765b80c60a2..6e6f246aeec 100644 --- a/llgo/third_party/gofrontend/libgo/go/database/sql/sql.go +++ b/llgo/third_party/gofrontend/libgo/go/database/sql/sql.go @@ -13,12 +13,12 @@ package sql import ( - "container/list" "database/sql/driver" "errors" "fmt" "io" "runtime" + "sort" "sync" ) @@ -37,6 +37,21 @@ func Register(name string, driver driver.Driver) { drivers[name] = driver } +func unregisterAllDrivers() { + // For tests. + drivers = make(map[string]driver.Driver) +} + +// Drivers returns a sorted list of the names of the registered drivers. +func Drivers() []string { + var list []string + for name := range drivers { + list = append(list, name) + } + sort.Strings(list) + return list +} + // RawBytes is a byte slice that holds a reference to memory owned by // the database itself. After a Scan into a RawBytes, the slice is only // valid until the next call to Next, Scan, or Close. @@ -198,8 +213,8 @@ type DB struct { dsn string mu sync.Mutex // protects following fields - freeConn *list.List // of *driverConn - connRequests *list.List // of connRequest + freeConn []*driverConn + connRequests []chan connRequest numOpen int pendingOpens int // Used to signal the need for new connections @@ -232,9 +247,6 @@ type driverConn struct { inUse bool onPut []func() // code (with db.mu held) run when conn is next returned dbmuClosed bool // same as closed, but guarded by db.mu, for connIfFree - // This is the Element returned by db.freeConn.PushFront(conn). - // It's used by connIfFree to remove the conn from the freeConn list. - listElem *list.Element } func (dc *driverConn) releaseConn(err error) { @@ -437,8 +449,6 @@ func Open(driverName, dataSourceName string) (*DB, error) { openerCh: make(chan struct{}, connectionRequestQueueSize), lastPut: make(map[*driverConn]string), } - db.freeConn = list.New() - db.connRequests = list.New() go db.connectionOpener() return db, nil } @@ -469,17 +479,13 @@ func (db *DB) Close() error { } close(db.openerCh) var err error - fns := make([]func() error, 0, db.freeConn.Len()) - for db.freeConn.Front() != nil { - dc := db.freeConn.Front().Value.(*driverConn) - dc.listElem = nil + fns := make([]func() error, 0, len(db.freeConn)) + for _, dc := range db.freeConn { fns = append(fns, dc.closeDBLocked()) - db.freeConn.Remove(db.freeConn.Front()) } + db.freeConn = nil db.closed = true - for db.connRequests.Front() != nil { - req := db.connRequests.Front().Value.(connRequest) - db.connRequests.Remove(db.connRequests.Front()) + for _, req := range db.connRequests { close(req) } db.mu.Unlock() @@ -527,11 +533,11 @@ func (db *DB) SetMaxIdleConns(n int) { db.maxIdle = db.maxOpen } var closing []*driverConn - for db.freeConn.Len() > db.maxIdleConnsLocked() { - dc := db.freeConn.Back().Value.(*driverConn) - dc.listElem = nil - db.freeConn.Remove(db.freeConn.Back()) - closing = append(closing, dc) + idleCount := len(db.freeConn) + maxIdle := db.maxIdleConnsLocked() + if idleCount > maxIdle { + closing = db.freeConn[maxIdle:] + db.freeConn = db.freeConn[:maxIdle] } db.mu.Unlock() for _, c := range closing { @@ -564,7 +570,7 @@ func (db *DB) SetMaxOpenConns(n int) { // If there are connRequests and the connection limit hasn't been reached, // then tell the connectionOpener to open new connections. func (db *DB) maybeOpenNewConnections() { - numRequests := db.connRequests.Len() - db.pendingOpens + numRequests := len(db.connRequests) - db.pendingOpens if db.maxOpen > 0 { numCanOpen := db.maxOpen - (db.numOpen + db.pendingOpens) if numRequests > numCanOpen { @@ -580,7 +586,7 @@ func (db *DB) maybeOpenNewConnections() { // Runs in a separate goroutine, opens new connections when requested. func (db *DB) connectionOpener() { - for _ = range db.openerCh { + for range db.openerCh { db.openNewConnection() } } @@ -616,7 +622,10 @@ func (db *DB) openNewConnection() { // connRequest represents one request for a new connection // When there are no idle connections available, DB.conn will create // a new connRequest and put it on the db.connRequests list. -type connRequest chan<- interface{} // takes either a *driverConn or an error +type connRequest struct { + conn *driverConn + err error +} var errDBClosed = errors.New("sql: database is closed") @@ -630,32 +639,21 @@ func (db *DB) conn() (*driverConn, error) { // If db.maxOpen > 0 and the number of open connections is over the limit // and there are no free connection, make a request and wait. - if db.maxOpen > 0 && db.numOpen >= db.maxOpen && db.freeConn.Len() == 0 { + if db.maxOpen > 0 && db.numOpen >= db.maxOpen && len(db.freeConn) == 0 { // Make the connRequest channel. It's buffered so that the // connectionOpener doesn't block while waiting for the req to be read. - ch := make(chan interface{}, 1) - req := connRequest(ch) - db.connRequests.PushBack(req) + req := make(chan connRequest, 1) + db.connRequests = append(db.connRequests, req) db.maybeOpenNewConnections() db.mu.Unlock() - ret, ok := <-ch - if !ok { - return nil, errDBClosed - } - switch ret.(type) { - case *driverConn: - return ret.(*driverConn), nil - case error: - return nil, ret.(error) - default: - panic("sql: Unexpected type passed through connRequest.ch") - } + ret := <-req + return ret.conn, ret.err } - if f := db.freeConn.Front(); f != nil { - conn := f.Value.(*driverConn) - conn.listElem = nil - db.freeConn.Remove(f) + if c := len(db.freeConn); c > 0 { + conn := db.freeConn[0] + copy(db.freeConn, db.freeConn[1:]) + db.freeConn = db.freeConn[:c-1] conn.inUse = true db.mu.Unlock() return conn, nil @@ -702,9 +700,15 @@ func (db *DB) connIfFree(wanted *driverConn) (*driverConn, error) { if wanted.inUse { return nil, errConnBusy } - if wanted.listElem != nil { - db.freeConn.Remove(wanted.listElem) - wanted.listElem = nil + idx := -1 + for ii, v := range db.freeConn { + if v == wanted { + idx = ii + break + } + } + if idx >= 0 { + db.freeConn = append(db.freeConn[:idx], db.freeConn[idx+1:]...) wanted.inUse = true return wanted, nil } @@ -793,18 +797,23 @@ func (db *DB) putConn(dc *driverConn, err error) { // If a connRequest was fulfilled or the *driverConn was placed in the // freeConn list, then true is returned, otherwise false is returned. func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { - if db.connRequests.Len() > 0 { - req := db.connRequests.Front().Value.(connRequest) - db.connRequests.Remove(db.connRequests.Front()) - if err != nil { - req <- err - } else { + if c := len(db.connRequests); c > 0 { + req := db.connRequests[0] + // This copy is O(n) but in practice faster than a linked list. + // TODO: consider compacting it down less often and + // moving the base instead? + copy(db.connRequests, db.connRequests[1:]) + db.connRequests = db.connRequests[:c-1] + if err == nil { dc.inUse = true - req <- dc + } + req <- connRequest{ + conn: dc, + err: err, } return true - } else if err == nil && !db.closed && db.maxIdleConnsLocked() > db.freeConn.Len() { - dc.listElem = db.freeConn.PushFront(dc) + } else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) { + db.freeConn = append(db.freeConn, dc) return true } return false @@ -1050,6 +1059,13 @@ type Tx struct { // or Rollback. once done, all operations fail with // ErrTxDone. done bool + + // All Stmts prepared for this transaction. These will be closed after the + // transaction has been committed or rolled back. + stmts struct { + sync.Mutex + v []*Stmt + } } var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") @@ -1071,6 +1087,15 @@ func (tx *Tx) grabConn() (*driverConn, error) { return tx.dc, nil } +// Closes all Stmts prepared for this transaction. +func (tx *Tx) closePrepared() { + tx.stmts.Lock() + for _, stmt := range tx.stmts.v { + stmt.Close() + } + tx.stmts.Unlock() +} + // Commit commits the transaction. func (tx *Tx) Commit() error { if tx.done { @@ -1078,8 +1103,12 @@ func (tx *Tx) Commit() error { } defer tx.close() tx.dc.Lock() - defer tx.dc.Unlock() - return tx.txi.Commit() + err := tx.txi.Commit() + tx.dc.Unlock() + if err != driver.ErrBadConn { + tx.closePrepared() + } + return err } // Rollback aborts the transaction. @@ -1089,8 +1118,12 @@ func (tx *Tx) Rollback() error { } defer tx.close() tx.dc.Lock() - defer tx.dc.Unlock() - return tx.txi.Rollback() + err := tx.txi.Rollback() + tx.dc.Unlock() + if err != driver.ErrBadConn { + tx.closePrepared() + } + return err } // Prepare creates a prepared statement for use within a transaction. @@ -1134,6 +1167,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) { }, query: query, } + tx.stmts.Lock() + tx.stmts.v = append(tx.stmts.v, stmt) + tx.stmts.Unlock() return stmt, nil } @@ -1162,7 +1198,7 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { dc.Lock() si, err := dc.ci.Prepare(stmt.query) dc.Unlock() - return &Stmt{ + txs := &Stmt{ db: tx.db, tx: tx, txsi: &driverStmt{ @@ -1172,6 +1208,10 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt { query: stmt.query, stickyErr: err, } + tx.stmts.Lock() + tx.stmts.v = append(tx.stmts.v, txs) + tx.stmts.Unlock() + return txs } // Exec executes a query that doesn't return rows. @@ -1333,15 +1373,12 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St return ci, releaseConn, s.txsi.si, nil } - var cs connStmt - match := false for i := 0; i < len(s.css); i++ { v := s.css[i] _, err := s.db.connIfFree(v.dc) if err == nil { - match = true - cs = v - break + s.mu.Unlock() + return v.dc, v.dc.releaseConn, v.si, nil } if err == errConnClosed { // Lazily remove dead conn from our freelist. @@ -1353,28 +1390,41 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St } s.mu.Unlock() - // Make a new conn if all are busy. - // TODO(bradfitz): or wait for one? make configurable later? - if !match { - dc, err := s.db.conn() - if err != nil { - return nil, nil, nil, err - } - dc.Lock() - si, err := dc.prepareLocked(s.query) - dc.Unlock() - if err != nil { - s.db.putConn(dc, err) - return nil, nil, nil, err + // If all connections are busy, either wait for one to become available (if + // we've already hit the maximum number of open connections) or create a + // new one. + // + // TODO(bradfitz): or always wait for one? make configurable later? + dc, err := s.db.conn() + if err != nil { + return nil, nil, nil, err + } + + // Do another pass over the list to see whether this statement has + // already been prepared on the connection assigned to us. + s.mu.Lock() + for _, v := range s.css { + if v.dc == dc { + s.mu.Unlock() + return dc, dc.releaseConn, v.si, nil } - s.mu.Lock() - cs = connStmt{dc, si} - s.css = append(s.css, cs) - s.mu.Unlock() } + s.mu.Unlock() + + // No luck; we need to prepare the statement on this connection + dc.Lock() + si, err = dc.prepareLocked(s.query) + dc.Unlock() + if err != nil { + s.db.putConn(dc, err) + return nil, nil, nil, err + } + s.mu.Lock() + cs := connStmt{dc, si} + s.css = append(s.css, cs) + s.mu.Unlock() - conn := cs.dc - return conn, conn.releaseConn, cs.si, nil + return dc, dc.releaseConn, si, nil } // Query executes a prepared query statement with the given arguments |