86 lines
1.5 KiB
Go
86 lines
1.5 KiB
Go
package sqladapter
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
var (
|
|
activeStatements int64
|
|
)
|
|
|
|
// Stmt represents a *sql.Stmt that is cached and provides the
|
|
// OnEvict method to allow it to clean after itself.
|
|
type Stmt struct {
|
|
*sql.Stmt
|
|
|
|
query string
|
|
mu sync.Mutex
|
|
|
|
count int64
|
|
dead bool
|
|
}
|
|
|
|
// NewStatement creates an returns an opened statement
|
|
func NewStatement(stmt *sql.Stmt, query string) *Stmt {
|
|
s := &Stmt{
|
|
Stmt: stmt,
|
|
query: query,
|
|
}
|
|
atomic.AddInt64(&activeStatements, 1)
|
|
return s
|
|
}
|
|
|
|
// Open marks the statement as in-use
|
|
func (c *Stmt) Open() (*Stmt, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if c.dead {
|
|
return nil, errors.New("statement is dead")
|
|
}
|
|
|
|
c.count++
|
|
return c, nil
|
|
}
|
|
|
|
// Close closes the underlying statement if no other go-routine is using it.
|
|
func (c *Stmt) Close() error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
c.count--
|
|
|
|
return c.checkClose()
|
|
}
|
|
|
|
func (c *Stmt) checkClose() error {
|
|
if c.dead && c.count == 0 {
|
|
// Statement is dead and we can close it for real.
|
|
err := c.Stmt.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Reduce active statements counter.
|
|
atomic.AddInt64(&activeStatements, -1)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnEvict marks the statement as ready to be cleaned up.
|
|
func (c *Stmt) OnEvict() {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
c.dead = true
|
|
c.checkClose()
|
|
}
|
|
|
|
// NumActiveStatements returns the global number of prepared statements in use
|
|
// at any point.
|
|
func NumActiveStatements() int64 {
|
|
return atomic.LoadInt64(&activeStatements)
|
|
}
|