Loading
Commits on Source 25
-
cznic authored
Documents the transpilation-based architecture (lib/, vec/, vfs/), the fragile modernc.org/libc version coupling, Makefile targets, debug build tags, the GitLab-canonical / GitHub-mirror workflow, and the singleton Driver registration model. Co-Authored-By:Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-
cznic authored
-
Closes #219. Mirrors the existing FileControlPersistWAL pattern: takes the schema name, allocates a 4-byte slot via the TLS allocator, invokes Xsqlite3_file_control with the SQLITE_FCNTL_DATA_VERSION opcode, and returns the resulting uint32. The returned data version changes whenever the contents of the database file change, so the typical caller polls it to invalidate application-level caches. Adds a regression test that opens a fresh file-backed database, queries the version, performs a commit on the same connection, performs another commit from a separate connection, and asserts the version moves both times.
-
cznic authored
-
Following the invitation in !115 review thread.
-
The []driver.Value slice passed to user-defined-function scalar and aggregate callbacks, and to vtab Filter / Insert / Update, was allocated fresh on every invocation. For queries that fan out a UDF over many rows this is the dominant per-row allocation, identified as a hot spot in #226 with profile data showing ~13.5% of allocations attributed to functionArgs. The driver's contract on FunctionImpl.Scalar and AggregateFunction.Step / WindowInverse already states the argument values are not valid past the return of the user function, so the slice itself can be reused safely. Add a sync.Pool of *[]driver.Value and route the five existing call sites through acquireUDFArgs / releaseUDFArgs: - funcTrampoline (scalar UDFs) - stepTrampoline (aggregate Step) - inverseTrampoline (aggregate WindowInverse) - vtabFilterTrampoline (vtab Filter) - vtabUpdateTrampoline (vtab Insert / Update) releaseUDFArgs zeroes each entry before returning the slice so any heap references held in the previous invocation can be reclaimed. Benchmark on 1000-row query with a 3-arg noop scalar UDF (Apple M3, Go 1.25, -benchtime 3s -count 3): name old time/op new time/op delta UDFArgsAllocation-8 213000 ns/op 205000 ns/op -4% name old alloc/op new alloc/op delta UDFArgsAllocation-8 118331 B/op 70376 B/op -40% name old allocs/op new allocs/op delta UDFArgsAllocation-8 6754 allocs/op 5754 allocs/op -15% The 1000 saved allocations per iteration match the expected savings: one slice header per UDF invocation, times 1000 invocations per query. Updates #226.
-
After !114 pools the []driver.Value slice across UDF and vtab callbacks, vtab Cursor.Filter and Updater.Insert/Update share the same "not valid past return" contract as FunctionImpl.Scalar / AggregateFunction.Step / WindowInverse. Document it explicitly so vtab implementations don't silently retain references and corrupt later invocations. Per cznic review on !114.
-
cznic authored
Co-Authored-By:Claude Opus 4.7 <noreply@anthropic.com>
-
The fix for #198 made (*conn).usable() return false whenever sqlite3_is_interrupted reports the connection is interrupted, so database/sql discards the connection after a context-cancelled query. For file-backed databases that is fine, the data is on disk and a new connection re-opens the same database. For in-memory databases the connection IS the database, so dropping it loses the entire store - re-introducing the regression originally fixed by !74 (issue #196). Detect at open time whether the database is in-memory by checking the output of sqlite3_db_filename and cache the result on the conn. usable() short-circuits to true for those connections so the post-interrupt one stays in the pool. File-backed connections keep the existing behaviour - they are still reported unusable after an interrupt. Adds TestInMemoryDBSurvivesContextCancel with two subtests: one that exercises the regression (insert rows, run a pre-cancelled query, re-query and assert the rows are still there), and one that asserts the file-backed path is still discarded after an explicit sqlite3_interrupt. Closes #196.
-
Per cznic's review on !116, the previous in-memory subtest used ExecContext with a pre-cancelled context, which short-circuits in stmt.exec before Xsqlite3_interrupt is ever called, so the table-still- present check passed even without the fix. Rewrite the subtest to obtain a *conn via db.Conn().Raw(), call sqlite3.Xsqlite3_interrupt directly, and assert c.usable() returns true (matches the file-backed subtest's shape). Retain the end-to-end QueryRow on the shared in-memory cache as a regression sanity check. Verified locally that the subtest fails when the c.inMemory short- circuit in (*conn).usable() is removed.
-
cznic authored
Co-Authored-By:Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-
Follow-up to !114 (#226). !114 pooled the []driver.Value slice header but explicitly left the per-row TEXT/BLOB body copies in place because a default-on zero-copy path would silently corrupt user code that retains an argument across rows -- undetectable by -race (UDF execution is sequential on one goroutine). This commit adds VolatileArgs bool to FunctionImpl as a strict opt-in. When true: - TEXT arguments are unsafe.String views into the SQLite-owned sqlite3_value_text buffer - BLOB arguments are unsafe.Slice views into the sqlite3_value_blob buffer When false (the default for all existing call sites), behavior is byte-for-byte identical to current master. Plumbing: the flag is captured at registration into small wrapper structs keyed in xFuncs.m / xAggregateFactories.m / xAggregateContext.m, so the hot path is one extra field read rather than a second map lookup. The five trampolines (funcTrampoline, stepTrampoline, inverseTrampoline, valueTrampoline, finalTrampoline) are updated for the new makeAggregate signature; only the three that materialise arguments forward the flag to functionArgs. Vtab Filter/Update are out of scope and pass false explicitly. Safety contract documented in detail on FunctionImpl.VolatileArgs: retention rule, deterministic-corruption failure mode invisible to -race, safe-copy idioms (strings.Clone / append([]byte(nil), ...)) for callbacks that must keep values across rows, and "when in doubt leave it off" guidance noting that the non-volatile path is already cheap after !114. Matching cross-references added to AggregateFunction.Step and WindowInverse docstrings. Benchmark (darwin/arm64 Apple M3, 3-arg noop UDF, 1000 rows of INTEGER + 5-char TEXT + 3-byte BLOB): BenchmarkUDFArgsAllocation-8 208360 ns/op 70368 B/op 5754 allocs/op BenchmarkUDFArgsAllocationVolatile-8 182444 ns/op 62371 B/op 3754 allocs/op The 2000 fewer allocs/op match the 1000 BLOB + 1000 TEXT copies that unsafe.Slice / unsafe.String skip. The remaining 3754 allocs are upstream of the trampoline. Tests: TestVolatileArgsScalar exercises the scalar path with TEXT, BLOB, empty-string, and NULL-coerced-to-empty-BLOB rows. TestVolatileArgsAggregate exercises the Step trampoline path. Both callbacks demonstrate the required safe-copy pattern (strings.Clone + append([]byte(nil), ...)). Existing UDF + aggregate + vtab tests stay green under -race. API shape and "explicit opt-in" framing suggested by @cznic in the !114 review thread.
-
Ian Chechin authored
Per @cznic on !120: 1. The volatile branch of functionArgs returned []byte(nil) for empty BLOB args, while the non-volatile branch returned make([]byte, 0). A user comparing args[i] == nil would see different results depending on the flag, which is orthogonal to the volatility contract. Switch the volatile branch to make([]byte, 0) so the empty-BLOB shape is identical across both modes. 2. Document the within-callback re-entrancy hazard in the VolatileArgs docstring: a nested Query/Exec on the same connection during a volatile-args callback can cause SQLite to reuse the underlying value buffers, so a volatile string/[]byte read before the nested call may alias different bytes after it returns. Rare in practice, but useful to spell out alongside the cross-row retention rule. TestVolatileArgsScalar and TestVolatileArgsAggregate stay green.
-
cznic authored
Co-Authored-By:Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-
cznic authored
Co-Authored-By:Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-
Ian Chechin authored
Follow-up to !120 (#226). !120 added the FunctionImpl.VolatileArgs opt-in for zero-copy TEXT/BLOB access on scalar and aggregate UDF callbacks but left vtab Filter/Update on the non-volatile path, flagged in the MR description as a candidate for a follow-up "if there's demand". This commit extends the same opt-in to: - Cursor.Filter (xFilter) - Updater.Insert / Updater.Update (xUpdate) The contract on those callbacks already says "the vals/cols slice and its entries are not valid past the return of this method; implementations must copy any value they wish to retain", so the API surface is unchanged for users who do not opt in; only the body-allocation strategy differs when opt-in is set. Opt-in is exposed as a new optional interface in package vtab: type VolatileArgsOpter interface { VolatileArgs() bool } A Module that implements it and returns true gets zero-copy TEXT/BLOB views for every Filter, Insert, and Upd...
-
cznic authored
-
cznic authored
Co-Authored-By:Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-
cznic authored