cache column metadata for prepared and cached statements
Column names and declared types are invariant for the lifetime of a prepared statement, but Columns()/declTypes() called C.sqlite3_column_name and sqlite3_column_decltype on every query, once per column. On hot QueryRow paths reusing an explicit Prepare or a _stmt_cache_size statement, this is a fixed set of cgo crossings paid on every execution. Cache the names and decltypes on the SQLiteStmt the first time they are materialized and reuse them on subsequent executions. Caching is gated by cacheMetadata(): explicit prepared statements always cache, Query-created ephemeral statements cache only when they live in the connection stmt cache. One-shot statements keep the previous per-call behavior.
This commit is contained in:
56
sqlite3.go
56
sqlite3.go
@@ -476,6 +476,12 @@ type SQLiteStmt struct {
|
|||||||
cls bool // True if the statement was created by SQLiteConn.Query
|
cls bool // True if the statement was created by SQLiteConn.Query
|
||||||
namedParams map[string][3]int
|
namedParams map[string][3]int
|
||||||
cacheKey string
|
cacheKey string
|
||||||
|
metadata *sqliteStmtMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqliteStmtMetadata struct {
|
||||||
|
cols []string
|
||||||
|
decltype []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SQLiteResult implements sql.Result.
|
// SQLiteResult implements sql.Result.
|
||||||
@@ -2475,6 +2481,36 @@ func (rc *SQLiteRows) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SQLiteStmt) cacheMetadata() bool {
|
||||||
|
return !s.cls || s.cacheKey != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLiteStmt) columnNamesLocked(n int) []string {
|
||||||
|
if s.metadata == nil {
|
||||||
|
s.metadata = &sqliteStmtMetadata{}
|
||||||
|
}
|
||||||
|
if len(s.metadata.cols) != n {
|
||||||
|
s.metadata.cols = make([]string, n)
|
||||||
|
for i := range s.metadata.cols {
|
||||||
|
s.metadata.cols[i] = C.GoString(C.sqlite3_column_name(s.s, C.int(i)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.metadata.cols
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLiteStmt) declTypesLocked(n int) []string {
|
||||||
|
if s.metadata == nil {
|
||||||
|
s.metadata = &sqliteStmtMetadata{}
|
||||||
|
}
|
||||||
|
if len(s.metadata.decltype) != n {
|
||||||
|
s.metadata.decltype = make([]string, n)
|
||||||
|
for i := range s.metadata.decltype {
|
||||||
|
s.metadata.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.s, C.int(i))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.metadata.decltype
|
||||||
|
}
|
||||||
|
|
||||||
// Columns return column names.
|
// Columns return column names.
|
||||||
func (rc *SQLiteRows) Columns() []string {
|
func (rc *SQLiteRows) Columns() []string {
|
||||||
if rc.s == nil {
|
if rc.s == nil {
|
||||||
@@ -2483,9 +2519,13 @@ func (rc *SQLiteRows) Columns() []string {
|
|||||||
rc.s.mu.Lock()
|
rc.s.mu.Lock()
|
||||||
defer rc.s.mu.Unlock()
|
defer rc.s.mu.Unlock()
|
||||||
if rc.s.s != nil && int(rc.nc) != len(rc.cols) {
|
if rc.s.s != nil && int(rc.nc) != len(rc.cols) {
|
||||||
rc.cols = make([]string, rc.nc)
|
if rc.s.cacheMetadata() {
|
||||||
for i := range rc.cols {
|
rc.cols = rc.s.columnNamesLocked(int(rc.nc))
|
||||||
rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i)))
|
} else {
|
||||||
|
rc.cols = make([]string, rc.nc)
|
||||||
|
for i := range rc.cols {
|
||||||
|
rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc.cols
|
return rc.cols
|
||||||
@@ -2493,9 +2533,13 @@ func (rc *SQLiteRows) Columns() []string {
|
|||||||
|
|
||||||
func (rc *SQLiteRows) declTypes() []string {
|
func (rc *SQLiteRows) declTypes() []string {
|
||||||
if rc.s.s != nil && rc.decltype == nil {
|
if rc.s.s != nil && rc.decltype == nil {
|
||||||
rc.decltype = make([]string, rc.nc)
|
if rc.s.cacheMetadata() {
|
||||||
for i := range rc.decltype {
|
rc.decltype = rc.s.declTypesLocked(int(rc.nc))
|
||||||
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
|
} else {
|
||||||
|
rc.decltype = make([]string, rc.nc)
|
||||||
|
for i := range rc.decltype {
|
||||||
|
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc.decltype
|
return rc.decltype
|
||||||
|
|||||||
Reference in New Issue
Block a user