Skip to content

Commit efa9b1c

Browse files
committed
add opt-in statement cache
1 parent 8d12439 commit efa9b1c

1 file changed

Lines changed: 124 additions & 11 deletions

File tree

sqlite3.go

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -445,12 +445,15 @@ type SQLiteDriver struct {
445445

446446
// SQLiteConn implements driver.Conn.
447447
type SQLiteConn struct {
448-
mu sync.Mutex
449-
db *C.sqlite3
450-
loc *time.Location
451-
txlock string
452-
funcs []*functionInfo
453-
aggregators []*aggInfo
448+
mu sync.Mutex
449+
db *C.sqlite3
450+
loc *time.Location
451+
txlock string
452+
funcs []*functionInfo
453+
aggregators []*aggInfo
454+
stmtCache map[string][]*SQLiteStmt
455+
stmtCacheSize int
456+
stmtCacheCount int
454457
}
455458

456459
// SQLiteTx implements driver.Tx.
@@ -467,6 +470,7 @@ type SQLiteStmt struct {
467470
closed bool
468471
cls bool // True if the statement was created by SQLiteConn.Query
469472
namedParams map[string][3]int
473+
cacheKey string
470474
}
471475

472476
// SQLiteResult implements sql.Result.
@@ -944,7 +948,7 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.Named
944948

945949
start := 0
946950
for {
947-
s, err := c.prepare(ctx, query)
951+
s, err := c.prepareWithCache(ctx, query, true)
948952
if err != nil {
949953
return nil, err
950954
}
@@ -1009,7 +1013,7 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro
10091013
func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
10101014
start := 0
10111015
for {
1012-
s, err := c.prepare(ctx, query)
1016+
s, err := c.prepareWithCache(ctx, query, true)
10131017
if err != nil {
10141018
return nil, err
10151019
}
@@ -1185,6 +1189,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
11851189
writableSchema := -1
11861190
vfsName := ""
11871191
var cacheSize *int64
1192+
stmtCacheSize := 0
11881193

11891194
pos := strings.IndexRune(dsn, '?')
11901195
if pos >= 1 {
@@ -1520,6 +1525,17 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
15201525
cacheSize = &iv
15211526
}
15221527

1528+
if val := params.Get("_stmt_cache_size"); val != "" {
1529+
iv, err := strconv.Atoi(val)
1530+
if err != nil {
1531+
return nil, fmt.Errorf("Invalid _stmt_cache_size: %v: %v", val, err)
1532+
}
1533+
if iv < 0 {
1534+
return nil, fmt.Errorf("Invalid _stmt_cache_size: %v, expecting non-negative integer", val)
1535+
}
1536+
stmtCacheSize = iv
1537+
}
1538+
15231539
if val := params.Get("vfs"); val != "" {
15241540
vfsName = val
15251541
}
@@ -1592,7 +1608,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
15921608
//
15931609

15941610
// Create connection to SQLite
1595-
conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
1611+
conn := &SQLiteConn{db: db, loc: loc, txlock: txlock, stmtCacheSize: stmtCacheSize}
1612+
if stmtCacheSize > 0 {
1613+
conn.stmtCache = make(map[string][]*SQLiteStmt)
1614+
}
15961615

15971616
// Password Cipher has to be registered before authentication
15981617
if len(authCrypt) > 0 {
@@ -1865,6 +1884,9 @@ func (c *SQLiteConn) Close() error {
18651884
return nil
18661885
}
18671886
runtime.SetFinalizer(c, nil)
1887+
if err := c.closeCachedStmtsLocked(); err != nil {
1888+
return err
1889+
}
18681890
rv := C.sqlite3_close_v2(c.db)
18691891
if rv != C.SQLITE_OK {
18701892
return lastError(c.db)
@@ -1883,12 +1905,85 @@ func (c *SQLiteConn) dbConnOpen() bool {
18831905
return c.db != nil
18841906
}
18851907

1908+
func (c *SQLiteConn) takeCachedStmt(query string) *SQLiteStmt {
1909+
if c == nil || query == "" {
1910+
return nil
1911+
}
1912+
1913+
c.mu.Lock()
1914+
defer c.mu.Unlock()
1915+
1916+
if c.db == nil || c.stmtCacheSize <= 0 {
1917+
return nil
1918+
}
1919+
stmts := c.stmtCache[query]
1920+
if len(stmts) == 0 {
1921+
return nil
1922+
}
1923+
s := stmts[len(stmts)-1]
1924+
if len(stmts) == 1 {
1925+
delete(c.stmtCache, query)
1926+
} else {
1927+
c.stmtCache[query] = stmts[:len(stmts)-1]
1928+
}
1929+
c.stmtCacheCount--
1930+
s.closed = false
1931+
s.cls = false
1932+
s.t = ""
1933+
return s
1934+
}
1935+
1936+
func (c *SQLiteConn) putCachedStmt(s *SQLiteStmt) bool {
1937+
if c == nil || s == nil || s.s == nil || s.cacheKey == "" {
1938+
return false
1939+
}
1940+
1941+
c.mu.Lock()
1942+
defer c.mu.Unlock()
1943+
1944+
if c.db == nil || c.stmtCacheSize <= 0 || c.stmtCacheCount >= c.stmtCacheSize {
1945+
return false
1946+
}
1947+
c.stmtCache[s.cacheKey] = append(c.stmtCache[s.cacheKey], s)
1948+
c.stmtCacheCount++
1949+
return true
1950+
}
1951+
1952+
func (c *SQLiteConn) closeCachedStmtsLocked() error {
1953+
for key, stmts := range c.stmtCache {
1954+
for _, s := range stmts {
1955+
if s == nil || s.s == nil {
1956+
continue
1957+
}
1958+
runtime.SetFinalizer(s, nil)
1959+
if rv := C.sqlite3_finalize(s.s); rv != C.SQLITE_OK {
1960+
return lastError(c.db)
1961+
}
1962+
s.s = nil
1963+
s.c = nil
1964+
}
1965+
delete(c.stmtCache, key)
1966+
}
1967+
c.stmtCacheCount = 0
1968+
return nil
1969+
}
1970+
18861971
// Prepare the query string. Return a new statement.
18871972
func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
18881973
return c.prepare(context.Background(), query)
18891974
}
18901975

18911976
func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, error) {
1977+
return c.prepareWithCache(ctx, query, false)
1978+
}
1979+
1980+
func (c *SQLiteConn) prepareWithCache(ctx context.Context, query string, useCache bool) (driver.Stmt, error) {
1981+
if useCache {
1982+
if stmt := c.takeCachedStmt(query); stmt != nil {
1983+
return stmt, nil
1984+
}
1985+
}
1986+
18921987
pquery := C.CString(query)
18931988
defer C.free(unsafe.Pointer(pquery))
18941989
var s *C.sqlite3_stmt
@@ -1902,6 +1997,9 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
19021997
t = strings.TrimSpace(C.GoString(tail))
19031998
}
19041999
ss := &SQLiteStmt{c: c, s: s, t: t}
2000+
if useCache && t == "" {
2001+
ss.cacheKey = query
2002+
}
19052003
runtime.SetFinalizer(ss, (*SQLiteStmt).Close)
19062004
return ss, nil
19072005
}
@@ -2014,11 +2112,26 @@ func (s *SQLiteStmt) Close() error {
20142112
runtime.SetFinalizer(s, nil)
20152113
conn := s.c
20162114
stmt := s.s
2017-
s.s = nil
2018-
s.c = nil
2115+
if stmt == nil {
2116+
s.c = nil
2117+
return nil
2118+
}
20192119
if !conn.dbConnOpen() {
20202120
return errors.New("sqlite statement with already closed database connection")
20212121
}
2122+
if s.cacheKey != "" {
2123+
rv := C._sqlite3_reset_clear(stmt)
2124+
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
2125+
s.s = nil
2126+
s.c = nil
2127+
return conn.lastError()
2128+
}
2129+
if conn.putCachedStmt(s) {
2130+
return nil
2131+
}
2132+
}
2133+
s.s = nil
2134+
s.c = nil
20222135
rv := C.sqlite3_finalize(stmt)
20232136
if rv != C.SQLITE_OK {
20242137
return conn.lastError()

0 commit comments

Comments
 (0)