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
- Replace len(args[start:start+na]) > 0 with na > 0 to avoid slice bounds check
- Use range loops instead of manual index loops for cols/decltype slices
- Use range variable v.Ordinal instead of re-indexing args[i].Ordinal
- Add bounds hint for decltype access in nextSyncLocked loop