1package squirrel
2
3import (
4	"database/sql"
5	"sync"
6)
7
8// Prepareer is the interface that wraps the Prepare method.
9//
10// Prepare executes the given query as implemented by database/sql.Prepare.
11type Preparer interface {
12	Prepare(query string) (*sql.Stmt, error)
13}
14
15// DBProxy groups the Execer, Queryer, QueryRower, and Preparer interfaces.
16type DBProxy interface {
17	Execer
18	Queryer
19	QueryRower
20	Preparer
21}
22
23// NOTE: NewStmtCacher is defined in stmtcacher_ctx.go (Go >= 1.8) or stmtcacher_noctx.go (Go < 1.8).
24
25type stmtCacher struct {
26	prep  Preparer
27	cache map[string]*sql.Stmt
28	mu    sync.Mutex
29}
30
31func (sc *stmtCacher) Prepare(query string) (*sql.Stmt, error) {
32	sc.mu.Lock()
33	defer sc.mu.Unlock()
34	stmt, ok := sc.cache[query]
35	if ok {
36		return stmt, nil
37	}
38	stmt, err := sc.prep.Prepare(query)
39	if err == nil {
40		sc.cache[query] = stmt
41	}
42	return stmt, err
43}
44
45func (sc *stmtCacher) Exec(query string, args ...interface{}) (res sql.Result, err error) {
46	stmt, err := sc.Prepare(query)
47	if err != nil {
48		return
49	}
50	return stmt.Exec(args...)
51}
52
53func (sc *stmtCacher) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
54	stmt, err := sc.Prepare(query)
55	if err != nil {
56		return
57	}
58	return stmt.Query(args...)
59}
60
61func (sc *stmtCacher) QueryRow(query string, args ...interface{}) RowScanner {
62	stmt, err := sc.Prepare(query)
63	if err != nil {
64		return &Row{err: err}
65	}
66	return stmt.QueryRow(args...)
67}
68
69type DBProxyBeginner interface {
70	DBProxy
71	Begin() (*sql.Tx, error)
72}
73
74type stmtCacheProxy struct {
75	DBProxy
76	db *sql.DB
77}
78
79func NewStmtCacheProxy(db *sql.DB) DBProxyBeginner {
80	return &stmtCacheProxy{DBProxy: NewStmtCacher(db), db: db}
81}
82
83func (sp *stmtCacheProxy) Begin() (*sql.Tx, error) {
84	return sp.db.Begin()
85}
86