| package squirrel |
| |
| import ( |
| "database/sql" |
| "fmt" |
| "sync" |
| ) |
| |
| // Prepareer is the interface that wraps the Prepare method. |
| // |
| // Prepare executes the given query as implemented by database/sql.Prepare. |
| type Preparer interface { |
| Prepare(query string) (*sql.Stmt, error) |
| } |
| |
| // DBProxy groups the Execer, Queryer, QueryRower, and Preparer interfaces. |
| type DBProxy interface { |
| Execer |
| Queryer |
| QueryRower |
| Preparer |
| } |
| |
| // NOTE: NewStmtCache is defined in stmtcacher_ctx.go (Go >= 1.8) or stmtcacher_noctx.go (Go < 1.8). |
| |
| // StmtCache wraps and delegates down to a Preparer type |
| // |
| // It also automatically prepares all statements sent to the underlying Preparer calls |
| // for Exec, Query and QueryRow and caches the returns *sql.Stmt using the provided |
| // query as the key. So that it can be automatically re-used. |
| type StmtCache struct { |
| prep Preparer |
| cache map[string]*sql.Stmt |
| mu sync.Mutex |
| } |
| |
| // Prepare delegates down to the underlying Preparer and caches the result |
| // using the provided query as a key |
| func (sc *StmtCache) Prepare(query string) (*sql.Stmt, error) { |
| sc.mu.Lock() |
| defer sc.mu.Unlock() |
| |
| stmt, ok := sc.cache[query] |
| if ok { |
| return stmt, nil |
| } |
| stmt, err := sc.prep.Prepare(query) |
| if err == nil { |
| sc.cache[query] = stmt |
| } |
| return stmt, err |
| } |
| |
| // Exec delegates down to the underlying Preparer using a prepared statement |
| func (sc *StmtCache) Exec(query string, args ...interface{}) (res sql.Result, err error) { |
| stmt, err := sc.Prepare(query) |
| if err != nil { |
| return |
| } |
| return stmt.Exec(args...) |
| } |
| |
| // Query delegates down to the underlying Preparer using a prepared statement |
| func (sc *StmtCache) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { |
| stmt, err := sc.Prepare(query) |
| if err != nil { |
| return |
| } |
| return stmt.Query(args...) |
| } |
| |
| // QueryRow delegates down to the underlying Preparer using a prepared statement |
| func (sc *StmtCache) QueryRow(query string, args ...interface{}) RowScanner { |
| stmt, err := sc.Prepare(query) |
| if err != nil { |
| return &Row{err: err} |
| } |
| return stmt.QueryRow(args...) |
| } |
| |
| // Clear removes and closes all the currently cached prepared statements |
| func (sc *StmtCache) Clear() (err error) { |
| sc.mu.Lock() |
| defer sc.mu.Unlock() |
| |
| for key, stmt := range sc.cache { |
| delete(sc.cache, key) |
| |
| if stmt == nil { |
| continue |
| } |
| |
| if cerr := stmt.Close(); cerr != nil { |
| err = cerr |
| } |
| } |
| |
| if err != nil { |
| return fmt.Errorf("one or more Stmt.Close failed; last error: %v", err) |
| } |
| |
| return |
| } |
| |
| type DBProxyBeginner interface { |
| DBProxy |
| Begin() (*sql.Tx, error) |
| } |
| |
| type stmtCacheProxy struct { |
| DBProxy |
| db *sql.DB |
| } |
| |
| func NewStmtCacheProxy(db *sql.DB) DBProxyBeginner { |
| return &stmtCacheProxy{DBProxy: NewStmtCache(db), db: db} |
| } |
| |
| func (sp *stmtCacheProxy) Begin() (*sql.Tx, error) { |
| return sp.db.Begin() |
| } |