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.
While `C.sizeof_unit` is 4, we convert to a Go uint and write it to the
pointer. The Go uint is eight bytes on 64 bit platforms.
Signed-off-by: Jakob Borg <jakob@kastelo.net>
sqlite3_prepare_v2 returns SQLITE_OK with a NULL statement handle when
the input contains no SQL. exec() already handled this; query() forwarded
the NULL handle to bind(), which crashed in sqlite3_clear_bindings(NULL).
Make query() skip NULL statements like exec() does, and make SQLiteRows
safe against a nil underlying statement so the empty-rows return value
does not crash.
Closes#1390
This avoids an unnecessary reset when the cache is full, guarantees
a statement cannot enter the cache without being reset/cleared, and
fixes a leak where sqlite3_finalize was not called when reset failed.
Finalize all cached statements even if one fails. Leaving a
finalized statement in the cache map would be a use-after-finalize
bug per SQLite documentation.
Move extern declarations for _sqlite3_*_blocking functions before
_sqlite3_exec_no_args which references them. Remove unused
_sqlite3_prepare_v2_nolen function.
- Add _sqlite3_exec_no_args() C function that combines prepare+step+finalize
into a single CGO crossing for parameterless exec (most common case)
- Add _sqlite3_reset_clear() C function that combines sqlite3_reset and
sqlite3_clear_bindings into a single CGO crossing
- Use semaphore channel instead of result struct channel in context-aware
exec/Next paths to reduce allocations
- Use time.AppendFormat with stack buffer to avoid heap allocation in
time.Time binding
- Optimize bindNamedIndices to reuse a single buffer instead of 3
separate C.CString allocations
- Remove intermediate bindIndices slice allocation in named parameter
binding path
- Pass explicit query length to sqlite3_prepare_v2 to avoid C-side strlen
benchstat (n=8):
BenchmarkExec: -29.44% sec/op, -50% B/op, -33% allocs/op
BenchmarkQuery: -9.83% sec/op
BenchmarkParams: -6.38% sec/op
geomean: -6.72% sec/op