Commits on Source 52

  • Ian Chechin's avatar
    add Backup.Remaining and Backup.PageCount progress wrappers · 5e633702
    Ian Chechin authored and cznic's avatar cznic committed
    Two thin wrappers around the existing sqlite3_backup_remaining and
    sqlite3_backup_pagecount C symbols. They expose the underlying backup
    progress counters that the database/sql layer already keeps but that
    Go callers cannot currently read without dropping to lib/* directly.
    
    The motivation is the standard progress-UI use case for online backups:
    
        for {
            more, err := bck.Step(pagesPerTick)
            if err != nil {
                return err
            }
            ui.Update(bck.PageCount()-bck.Remaining(), bck.PageCount())
            if !more {
                break
            }
        }
    
    Without these wrappers a caller has to either skip the progress display
    or fall back to unsafe per-call SQL queries against pragma_page_count.
    
    API shape mirrors !115 (FileControlDataVersion): named after the SQLite
    C function with the s/sqlite3_// prefix stripped and CamelCase applied,
    documented inline with a link to the official C API page, and added on
    the existing public...
    5e633702
  • cznic's avatar
    Merge branch 'feat/backup-progress-wrappers' into 'master' · 2cba7d51
    cznic authored
    add Backup.Remaining and Backup.PageCount progress wrappers
    
    See merge request !122
    2cba7d51
  • cznic's avatar
    CHANGELOG.md: document #122 · 0c32f40a
    cznic authored
    
    
    Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
    0c32f40a
  • Ian Chechin's avatar
    conn: skip the second string copy in columnText · 20ab6ab7
    Ian Chechin authored
    (*conn).columnText currently allocates twice per TEXT column per row:
    once for the make([]byte, len) buffer that receives the SQLite-owned UTF-8
    bytes, and once again inside the string(b) conversion that
    runtime.slicebytetostring performs because the compiler must assume the
    caller could mutate b.
    
    Here b is local to columnText and is never touched again after the copy
    from the C buffer, so the second copy is redundant. Replacing string(b)
    with unsafe.String(unsafe.SliceData(b), len) builds the returned string
    directly on top of b. The string is immutable from Go's perspective, the
    GC keeps b alive for as long as the string is reachable, and no aliasing
    is possible because b becomes unreachable as []byte the moment the
    function returns. The same pattern is already used in sqlite.go (!120)
    for the volatile-args path.
    
    Benchmark on darwin/arm64 (Apple M3), 1000-row SELECT of a single TEXT
    column, -benchtime=2s, before -> after:
    
      Short  (16-byte TEXT):
        4009 -> 4009 allocs/op  (Go runtime already short-circuits
                                 string(b) for slices below the inline
                                 threshold; no regression either)
           52348 ->    52348 B/op
          157342 ->   155746 ns/op
    
      Medium (256-byte TEXT):
        5009 -> 4009 allocs/op  (-1000 allocs/op = -1 per row)
          548351 ->   292350 B/op  (-256 KB/op = the second 256-byte copy)
          226863 ->   204730 ns/op (-10%)
    
      Long  (4096-byte TEXT):
        5009 -> 4009 allocs/op  (-1000 allocs/op = -1 per row)
         8228510 -> 4132413 B/op  (-4 MB/op = the second 4 KB copy)
         1605640 -> 1135113 ns/op (-29%)
    
    The saving scales linearly with TEXT column length, since the eliminated
    work is exactly one memcpy of the column bytes. No change to (*conn).
    columnBlob, which already returns its make([]byte, len) buffer directly
    and pays only one alloc + memcpy per row.
    
    TestColumnTextScan exercises the path under -race over the three branches
    of columnText: empty (short-circuit), short (Go-fast-path) and long
    (allocating) TEXT, including a multi-byte / emoji payload to confirm
    UTF-8 is preserved bit-for-bit. Full go test -count=1 ./... stays green.
    20ab6ab7
  • cznic's avatar
    Merge branch 'perf/column-text-zero-copy' into 'master' · c80a08fb
    cznic authored
    conn: skip the second string copy in columnText
    
    See merge request !123
    c80a08fb
  • cznic's avatar
    CHANGELOG.md: document #123 · b17c0c7f
    cznic authored
    
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    b17c0c7f
  • Ian Chechin's avatar
    rows: cache the column decltype lookup once per result set · f8fb6dd1
    Ian Chechin authored
    The Next() hot path calls (*rows).ColumnTypeDatabaseTypeName(i) for
    every TEXT column on every row when _texttotime=1, and for every
    INTEGER column on every row when intToTime is set. Each call ran:
    
      return strings.ToUpper(r.c.columnDeclType(r.pstmt, index))
    
    which is one libc.GoString to materialise the C decltype string into Go
    memory, plus a (cheap, allocation-free for already-uppercase inputs)
    strings.ToUpper. The declared type of a result column is fixed for the
    lifetime of a prepared statement, so the libc.GoString cost is paid
    N_text_cols * N_rows times for nothing.
    
    Move the lookup to newRows() and cache the uppercased decltype into a
    new rows.decltypes []string. ColumnTypeDatabaseTypeName, the Next()
    DATETIME branch (which goes through it), and ColumnTypeScanType now
    read from the cache instead of redoing the C round-trip per row. The
    case-sensitive switch in ColumnTypeScanType is rewritten against the
    cached uppercase values to drop a per-column strings.ToLower as well.
    
    Benchmark (darwin/arm64 Apple M3, _texttotime=1, 1000-row SELECT of all
    DATETIME columns, -benchtime=2s, before -> after):
    
      1 column:
        11010 -> 10012 allocs/op  (-1000 = -1 per row, the libc.GoString)
           400354 ->   392393 B/op  (-8 KB = -8 bytes per row, "DATETIME"
                                      string body)
           646068 ->   601121 ns/op (-7%)
    
      5 columns:
        55014 -> 50020 allocs/op  (-5000 = -5 per row, -1 per col per row)
          2000499 -> 1960654 B/op  (-40 KB, scales linearly with columns)
          2992839 -> 2908393 ns/op (-3%)
    
    The saving scales 1:1 with N_text_cols * N_rows for queries that hit
    the time-conversion path. Workloads using _texttotime, _time_format,
    or _intToTime DSN flags benefit; queries without those flags do not
    touch ColumnTypeDatabaseTypeName per row and see no behavior change.
    
    TestColumnTypeDatabaseTypeNameCache covers a mixed-case CREATE TABLE
    across all SQLite storage classes (INTEGER / TEXT / BLOB / DATETIME /
    DATE / BOOLEAN), reads the cache once at result-set start and again
    inside the Next loop for every row, and asserts the values never drift.
    The full go test -count=1 ./... suite stays green.
    f8fb6dd1
  • Ian Chechin's avatar
    rows: lock down ColumnTypeScanType under the decltype cache · 8a6f33ce
    Ian Chechin authored
    Per @cznic on !124: the decltype cache rewrites the lowercase decltype
    switch in ColumnTypeScanType to a cached-uppercase switch, but the
    existing TestColumnTypeDatabaseTypeNameCache only exercises the
    DatabaseTypeName side. Add a table-driven TestColumnTypeScanTypeDecltypeCache
    that covers every arm of the cached switch:
    
      - INTEGER + BOOLEAN (any case)              -> bool
      - INTEGER + DATE/DATETIME/TIME/TIMESTAMP    -> time.Time
      - INTEGER + plain / unrecognised decltype   -> int64
      - TEXT (default)                            -> string
      - TEXT + DATETIME-shaped decltype (no flag) -> string
      - TEXT + DATE/DATETIME/TIME/TIMESTAMP under _texttotime=1 -> time.Time
      - TEXT + unrecognised decltype under _texttotime=1        -> string
    
    Each case uses a mixed-case declared type to keep the case-folding path
    covered, and inserts one row before SELECT so sqlite3_column_type sees
    the actual storage class instead of SQLITE_NULL (which would short-
    circuit ColumnTypeScanType to reflect.TypeOf(nil)).
    
    All 15 sub-cases pass under -race.
    8a6f33ce
  • cznic's avatar
    Merge branch 'perf/cache-column-decltype' into 'master' · 51e67147
    cznic authored
    rows: cache the column decltype lookup once per result set
    
    See merge request !124
    51e67147
  • cznic's avatar
    CHANGELOG.md: document #124 · 7da793ef
    cznic authored
    
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    7da793ef
  • Ian Chechin's avatar
    rows: cache the parseTime format index per result column · 3638d17b
    Ian Chechin authored
    (*conn).parseTime ran on every TEXT-stored DATETIME / DATE / TIMESTAMP
    column read in Next(). The function tried (*conn).parseTimeString first
    and then walked parseTimeFormats[0..6] sequentially until time.Parse
    matched the row's value. For the canonical SQLite TEXT datetime format
    ("2006-01-02 15:04:05.999999999", index 2) every row paid two failed
    time.Parse attempts in the warmup, plus the one successful match. Each
    failed Parse allocates a ParseError, so the per-row cost on a steady
    1000-row scan was ~5 allocs per row from the format-search alone.
    
    Add a sticky per-column hint cache:
    
      - rows.parseFmtIdx []int8, sized once at newRows() to the column count,
        initialised to -1 (no match recorded).
      - (*conn).parseTime now takes hintIdx int and returns the index that
        actually matched (or -1 when parseTimeString matched / all formats
        failed). It tries hintIdx first if in range, then walks the list
        skipping the index it just tried.
      - rows.Next() records the first successful index per column and reuses
        it on subsequent rows. The cache is sticky: it is set once and not
        overwritten, so a mixed-format column still pays the original
        fallthrough cost on non-matching rows but a steady column wins on
        every row after the first.
    
    Benchmark (darwin/arm64 Apple M3, 1000-row SELECT of a DATETIME TEXT
    column in the canonical SQLite format, -benchtime=2s, before -> after):
    
      10013 -> 5019 allocs/op   (-50%, ~5 fewer per row)
       392417 -> 168672 B/op    (-57%, mostly ParseError structs)
       633531 -> 397843 ns/op   (-37%)
    
    TestParseTimeFormatCache covers correctness across the cache transitions:
    three steady-format rows followed by one ISO-T format row (different
    index) and one date-only row (yet another index), all returning the
    expected time.Time. The full go test -count=1 ./... suite stays green.
    
    No API change. The fall-through chain is preserved bit-for-bit so any
    row the old code would have parsed still parses to the same value.
    3638d17b
  • cznic's avatar
    Merge branch 'perf/cache-parse-time-format' into 'master' · 44857934
    cznic authored
    rows: cache the parseTime format index per result column
    
    See merge request !125
    44857934
  • cznic's avatar
    rows: clarify parseFmtIdx mixed-column cost; CHANGELOG.md: document #125 · e3f64ec2
    cznic authored
    
    
    Tighten the parseFmtIdx doc comment: a mixed-format column pays at most one extra format probe (on rows whose matching format precedes the cached index), not just the original fall-through cost. Add the !125 CHANGELOG entry. No code/behavior change.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    e3f64ec2
  • cznic's avatar
    release v1.52.0, upgrade to SQLite 3.53.2 · 66b4d20f
    cznic authored
    66b4d20f
  • Ian Chechin's avatar
    sqlite: add SQLITE_CONFIG_PCACHE2 wrapper (draft API + skeleton) · 277a67de
    Ian Chechin authored
    
    
    Adds the public PageCacheModule API, the SQLITE_CONFIG_PCACHE2 wiring,
    the lifecycle gate between RegisterPageCacheModule and the first Open,
    and tests for the API surface. The production pool-backed reference
    implementation and the memory-utilization benchmark that demonstrate
    the reduction in #204 are deferred to a follow-up MR so the API shape
    can be reviewed independently of the implementation.
    
    New files:
    
    - pagecache.go: public PageCacheModule struct, Page / PageEq interfaces,
      RegisterPageCacheModule / MustRegisterPageCacheModule entry points,
      ErrPageCacheTooLate / ErrPageCacheConflict sentinels.
    - pagecache_alias_new.go, pagecache_alias_old.go: per-arch type alias
      shims so pagecache.go can refer to a single pcacheMethods2 type.
      Most arches emit the SQLite struct as Tsqlite3_pcache_methods2; the
      three old-generator arches (freebsd_386, freebsd_arm, netbsd_amd64)
      emit it as Sqlite3_pcache_methods2. The two build-tagged shims keep
      the body of pagecache.go arch-agnostic.
    - pagecache_test.go: TestPCacheMethods2Layout pins the struct shape
      against regeneration drift, TestRegisterPageCacheModuleValidation
      covers nil / missing-required-field rejection,
      TestRegisterPageCacheModuleLifecycle exercises the gate
      (conflict, too-late, same-pointer idempotency), and
      TestOpenGateConcurrentReaders runs the openGate RWMutex under load.
    
    Modified files:
    
    - conn.go: newConn now wraps c.openV2 in withOpenGate so an
      in-flight Open holds pcacheState.openGate.RLock for the duration
      of sqlite3_open_v2. A concurrent RegisterPageCacheModule call
      takes the write lock, drains all readers, and then either
      installs the methods table or returns ErrPageCacheTooLate.
    
    Design notes:
    
    - SQLITE_CONFIG_PCACHE2 is global and one-shot. The first successful
      install commits pcacheState.registered = m and pcacheState.cMethods;
      any subsequent call with a different pointer returns
      ErrPageCacheConflict, with the same pointer returns nil. Reload is
      not supported in this MR.
    - The configOnce.Do body uses a defer / recover guard so a panic
      during Xsqlite3_config or populateCMethods leaves configErr set
      to a panic message and rolls back the half-set state.
    - populateCMethods uses named-field writes (FiVersion, FpArg,
      FxInit, ...) rather than hardcoded byte offsets, so the wiring
      is portable across all supported GOOS / GOARCH pairs.
    - Page / PageEq interfaces are exported as a stable surface for
      follow-up impl authors; the binding itself does not use them in
      this MR.
    
    Tested locally on darwin/arm64 with go test -race -short. Cross-build
    clean for linux/amd64, linux/386, linux/arm64, darwin/arm64,
    darwin/amd64, windows/amd64, openbsd/amd64; existing pre-MR upstream
    build failures on freebsd/386, freebsd/arm, netbsd/amd64 are
    unrelated to this change.
    
    Updates #204
    
    Signed-off-by: default avatarIan Chechin <ian00chechin@gmail.com>
    277a67de
  • Ian Chechin's avatar
    sqlite: pcache2 rework per !126 review (idiomatic Go API, internal... · 84e273a9
    Ian Chechin authored
    sqlite: pcache2 rework per !126 review (idiomatic Go API, internal trampolines, binding-owned stubs)
    
    Replaces the raw-ccgo-ABI surface from the prior commit with an
    idiomatic Go interface set, per the maintainer's review at
    gitlab.com/cznic/sqlite/-/merge_requests/126#note. The wiring,
    lifecycle gate, and tests are kept and extended; the user-facing
    surface is rebuilt.
    
    Public API changes:
    
    - PageCache (factory) replaces PageCacheModule. Single method:
      Create(pageSize, extraSize int, purgeable bool) (Cache, error).
    - Cache (per-database instance) replaces the prior PageCacheModule
      struct of function fields. All methods are required, which
      eliminates the SIGSEGV class the maintainer reproduced: a module
      with nil Rekey or Shrink. Methods: SetSize, PageCount, Fetch,
      Unpin, Rekey, Truncate, Destroy, Shrink.
    - Page (unchanged) is the raw-memory boundary the binding cannot
      hide: SQLite reads pBuf/pExtra directly and the addresses must
      stay put.
    - FetchMode enum (FetchLookup / FetchCreateEasy / Fet...
    84e273a9
  • Ian Chechin's avatar
    sqlite: pcache2 round-2 fixes per !126 review (always-call-Fetch, doc... · 982cdc2d
    Ian Chechin authored
    
    sqlite: pcache2 round-2 fixes per !126 review (always-call-Fetch, doc tightening, CHANGELOG + doc.go)
    
    Addresses cznic's second-round review at
    gitlab.com/cznic/sqlite/-/merge_requests/126#note_3434353434. Three
    contract changes plus housekeeping.
    
    1. Always-call-Fetch design (pagecache_trampolines.go)
    
       pcacheTrampolineFetch now invokes Cache.Fetch on every SQLite
       request and reuses the cached sqlite3_pcache_page stub only when
       the returned Page value equals the previously-stored Page for that
       key. When the implementation evicted the entry and returned either
       nil or a different Page, the binding retires the stale stub via
       libc.Xfree and either returns NullStub (impl reports miss) or
       mints a fresh stub (impl returned a new Page).
    
       This unblocks the canonical use case for plugging in a custom page
       cache: a bounded purgeable cache that evicts on Unpin(discard=false)
       to honour cache_size. The previous design's stub-caching shortcut
       silently leaked a stale stub to SQLite the next time the same key
       was fetched, because the binding short-circuited before consulting
       the impl. The reshape removes that hazard by construction.
    
    2. Drop Unpin/Rekey re-check pattern; document threading invariant
    
       pagecache_trampolines.go now states the invariant on pcacheBinding
       explicitly: all callbacks for a single Cache are serialised by the
       SQLite engine because every connection opens
       SQLITE_OPEN_FULLMUTEX, the driver does not use shared-cache mode,
       and database/sql never invokes one driver.Conn from two goroutines.
       Under that invariant the Truncate-vs-Unpin/Rekey races the prior
       commit's re-check pattern guarded cannot occur, so Unpin and Rekey
       are back to a single user-callback-outside-lock + bookkeeping-
       inside-lock shape. The Cache godoc is tightened to match: it no
       longer asks implementations to provide per-Cache concurrency
       protection that the binding now guarantees they do not need.
    
    3. Page docstring and Cache.Unpin docstring
    
       Page now states the off-heap memory requirement plainly and
       forbids Go-heap memory including runtime.Pinner-pinned slices.
       cznic reproduced a checkptr failure under -race with runtime.Pinner:
       Buf and Extra round-trip through the C stub as machine words and
       lose checkptr provenance, then SQLite performs interior pointer
       arithmetic on Extra (the PgHdr lives at its head), which trips
       fatal error: checkptr: pointer arithmetic result points to invalid
       allocation in _sqlite3PcacheFetchFinish. The accepted backings are
       now libc.Xmalloc, sqlite3_malloc, mmap, and similar off-heap
       allocators.
    
       Page also documents that it must be a comparable type (the binding
       compares Page values to detect retain-vs-replace across Fetches);
       pointer-backed implementations satisfy this automatically.
    
       Cache.Unpin loses the "stricter than SQLite" framing it carried in
       the prior commit. The contract is the standard one: a page must
       stay valid and at a stable address only while pinned (between the
       Fetch that returned it and the matching Unpin); the implementation
       is free to evict an unpinned page at its discretion, and the next
       Fetch for the same key will be consulted afresh.
    
    4. CHANGELOG.md and doc.go
    
       CHANGELOG.md gains a TBC vNEXT entry summarising the wrapper for
       the next release. doc.go gains a short "Pluggable page cache"
       section near the top with godoc links to RegisterPageCache,
       PageCache, Cache, and Page so the feature is discoverable from
       the package overview.
    
    Tested locally on darwin/arm64 with go test -race -short. Cross-build
    clean for linux/amd64, linux/386, linux/arm64, darwin/arm64,
    darwin/amd64, windows/amd64, openbsd/amd64, freebsd/amd64. Existing
    non-pcache tests unchanged and green. The pre-existing upstream build
    failures on freebsd/386, freebsd/arm, netbsd/amd64 are unchanged and
    unrelated.
    
    Refs !126 round-2 review
    Updates #204
    
    Signed-off-by: default avatarIan Chechin <ian00chechin@gmail.com>
    982cdc2d
  • cznic's avatar
    Merge branch 'pcache2-api-draft' into 'master' · ebeeb1da
    cznic authored
    sqlite: add SQLITE_CONFIG_PCACHE2 wrapper (API + skeleton)
    
    Closes #204
    
    See merge request !126
    ebeeb1da
  • Ian Chechin's avatar
    pcache: pool-backed Cache impl with bounded LRU + #204 benchmark + e2e harness · f3004385
    Ian Chechin authored
    Implements the production page cache deferred to MR-B from !126.
    
    pool.go: PageCache factory minting per-database caches backed by
    libc.Xmalloc / libc.Xcalloc pages with a strict cache_size cap and
    LRU-tail eviction. Page identity is *page (comparable). Pool aggregates
    hit/miss/alloc/eviction counters across every cache it creates.
    
    pool_test.go: 16 unit tests covering empty state, retain/replace across
    Fetch cycles, FetchCreateEasy refusing at cap, FetchCreateForce evicting
    LRU tail, SetSize shrink + overcommit-on-pinned, Rekey with colliding
    eviction, Truncate pinned eviction, Shrink, Destroy no-panic.
    
    e2e_test.go: real-DB harness mirroring the !126 validation workload:
    cache_size=16, 4000 BLOB rows + DELETE + incremental_vacuum,
    integrity_check=ok under -race. Multi-DB test asserts one Cache per
    opened database.
    
    bench_test.go: BenchmarkPoolBoundedCache reports per-insert allocs +
    evictions + go-heap-inuse delta for #204 memory-utilization
    measurement. BenchmarkPoolEvictionChurn drives steady-state 1:1
    alloc/eviction churn at cache_size=16.
    f3004385
  • cznic's avatar
    Merge branch 'pcache2-impl-pool' into 'master' · 9e09aac4
    cznic authored
    pcache: add pool-backed PageCache implementation + #204 memory benchmark
    
    See merge request !127
    9e09aac4
  • Ian Chechin's avatar
    sqlite: add _dqs opt-in DSN parameter (#61) · 3690a8e6
    Ian Chechin authored and cznic's avatar cznic committed
    An opt-in `_dqs` DSN query parameter disables SQLite's double-quoted
    string literal compatibility quirk on a per-connection basis. When set
    to a false value (`_dqs=0` or any `strconv.ParseBool` false), the
    driver calls `sqlite3_db_config` with both `SQLITE_DBCONFIG_DQS_DDL`
    and `SQLITE_DBCONFIG_DQS_DML` set to off in `newConn`, after
    `sqlite3_open_v2` and before any statement is prepared. Default
    (absent or `_dqs=1`) leaves SQLite's built-in behavior unchanged so
    existing DSNs continue to work byte-for-byte.
    
    The config call goes through a new `(*conn).dbConfigBool` helper that
    hand-lays the mixed `(int onoff, int *pRes)` vararg form for the
    cgo-free transpilation: two pointer-sized slots, the second a NULL
    pointer because callers in this driver only need the side effect.
    
    Tests cover the FFI shape directly (`TestDQSConfigCallVaList`),
    end-to-end behavior through `database/sql` for the default,
    explicit-on, and off cases (`TestDQSOptIn`), and the unparseable-value
    error path (`TestDQSInvalid`).
    
    Documented next to `_pragma` and `_txlock` in driver.go. CHANGELOG
    entry under TBC vNEXT.
    
    Resolves #61.
    3690a8e6
  • cznic's avatar
  • cznic's avatar
    Merge branch 'master' into 'dqs-opt-in' · 7807cf08
    cznic authored
    # Conflicts:
    #   CHANGELOG.md
    7807cf08
  • cznic's avatar
    sqlite: fix 32-bit va_list buffer sizing in dbConfigBool (#61) · 39a0c0b1
    cznic authored
    
    
    dbConfigBool sized its va_list buffer as 2*unsafe.Sizeof(uintptr(0)),
    i.e. 8 bytes on 32-bit targets. libc.VaList packs every argument into a
    fixed 8-byte slot regardless of pointer width (an int is widened to 8
    bytes), so the (int onoff, int *pRes) pair writes 12 bytes on 32-bit
    (int64 at [0,8), pointer at [8,12)) and overruns the 8-byte allocation
    by 4 bytes on linux/386 and linux/arm. The overflow is currently masked
    by modernc.org/memory allocator over-allocation and is caught by neither
    go vet nor cross-builds; -race is unavailable on 386.
    
    Size the buffer for two 8-byte VaList slots. Verified: a fill-and-probe
    shows VaList writes [0,12) within a 16-byte buffer on 386; TestDQS* pass
    on amd64 and 386; full -short suite green.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    39a0c0b1
  • cznic's avatar
    CHANGELOG.md: restore _dqs entry dropped in master merge (#61, !128) · 0560e005
    cznic authored
    
    
    The _dqs changelog entry added by the original !128 commit was lost when
    master was merged into the dqs-opt-in branch and the CHANGELOG.md
    conflict was resolved in master's favor. Re-add it under the v1.53.0
    section.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    0560e005
  • cznic's avatar
    Merge branch 'dqs-opt-in' into 'master' · 267290b6
    cznic authored
    sqlite: add _dqs opt-in DSN parameter (#61)
    
    Closes #61
    
    See merge request !128
    267290b6
  • Ian Chechin's avatar
    sqlite: add _error_rc opt-in DSN parameter (#230) · 1cf7251e
    Ian Chechin authored
    An opt-in `_error_rc` DSN query parameter switches the connection into
    a stricter error-string reporting mode: when set to a true value
    (`_error_rc=1` or any `strconv.ParseBool` true), the synthesised
    `*Error.Error()` only appends `sqlite3_errmsg(db)` when
    `sqlite3_extended_errcode(db)` is consistent with the operation rc.
    The match check tries the full extended code first and falls back to
    the primary code (`rc & 0xff`); on mismatch the canonical
    `sqlite3_errstr(rc)` is used alone. This stops the legacy
    `errstr: errmsg` form from carrying a stale errmsg from the temporary
    db handle on open-time failures: a SQLITE_CANTOPEN no longer reads as
    "unable to open database file: out of memory" when the temporary
    handle was last touched by an unrelated initialisation path.
    
    Absence of the parameter or `_error_rc=0` preserves the legacy form
    byte-for-byte so callers parsing error strings remain unaffected.
    `*Error.Code()` returns the same SQLite result code in both modes;
    the cznic review refinement was to change the message only, never
    the code.
    
    The parameter is parsed before `sqlite3_open_v2` because the failure
    path that motivated the issue runs during open itself, on a
    temporary db handle. A new `getErrorRcMode` parser sits next to
    `getVFSName` in sqlite.go and is consulted before the `conn` struct
    is created. The flag is then stored on `conn.errorRcMode` and
    threaded through `errstrForDB`'s new `errorRcMode bool` parameter to
    the three call sites (`(*conn).openV2`, `(*conn).errstr`, and the
    backup-init error path which uses the destination connection's mode).
    
    Tests cover the open-time SQLITE_CANTOPEN reproducer in all three
    modes (default, explicit-off, on) with structural assertions that
    hold across platforms; the non-regression case where syntax errors
    must preserve the helpful "no such table" detail in every mode; and
    the invalid-value error path.
    
    Documented next to `_pragma`, `_txlock`, and `_dqs` in driver.go.
    CHANGELOG entry under TBC vNEXT.
    
    Resolves #230.
    1cf7251e
  • Ian Chechin's avatar
    pcache: address !127 review follow-ups (Stats accuracy + EasyRefusals counter) · f64de56c
    Ian Chechin authored and cznic's avatar cznic committed
    Three non-blocking follow-ups raised by cznic on the !127 merge,
    collected into a single polish pass:
    
    (1) Stats.Evictions documentation tightened to match actual behavior.
    The field counts LRU eviction, Unpin(discard=true), and Shrink releases;
    bulk frees performed by Truncate, Rekey collisions, and Destroy are
    not counted. The old "LRU-driven page releases" docstring read
    narrower than the implementation.
    
    (2) New Stats.EasyRefusals counter. FetchCreateEasy refuses at cap
    even when there are recyclable unpinned pages, while pcache1 would
    recycle one without spilling; the counter records each refusal so
    the I/O pressure of the strict Easy contract is observable. SQLite
    reacts to a refusal by spilling dirty pages and retrying with
    FetchCreateForce, so EasyRefusals/op is a direct proxy for that
    spill rate. The two existing benchmarks now report easy-refusals/op
    alongside the page-allocs and page-evictions metrics, and
    TestEasyHonoursCacheSize asserts the counter increments on each
    Easy refusal.
    
    (3) TestPoolRoundTripIntegrity comment fix. The previous wording
    claimed the DELETE + incremental_vacuum workload exercised xRekey
    ~15 times, which the actual run does not confirm (Rekeys reports 0
    on every platform). The corrected comment notes that the SQL surface
    does not reliably emit xRekey here and that the code path is covered
    by the unit tests (TestRekey, TestRekeyEvictsCollider) instead.
    
    Open question for !127 follow-up I/O comparison: a direct side-by-side
    vs the in-engine pcache1 (e.g. SQLITE_DBSTATUS_CACHE_SPILL) would
    require either exposing sqlite3_db_status through the parent driver
    or running two separate test binaries with and without
    sqlite.MustRegisterPageCache. Asking which approach you prefer before
    extending the benchmark in that direction; for now EasyRefusals/op is
    the in-package proxy.
    
    Builds clean across the 8 supported GOOS/GOARCH pairs, gofmt clean,
    go vet only the two pre-existing unsafe.Pointer notices on Buf/Extra.
    go test -race -short ./pcache/ ok (16 unit + 2 e2e + new
    TestEasyHonoursCacheSize assertions).
    f64de56c
  • Ian Chechin's avatar
    pcache: scaffolding for cross-connection / shared-cache support (RFC) · 6250b755
    Ian Chechin authored and cznic's avatar cznic committed
    Adds an empty sharing.go that captures the three open design
    questions for MR-C. The file compiles but introduces no behavior;
    it exists so cznic can react to the directional choices in a focused
    diff before any concurrency primitive, wrapper type, or PageCache
    contract change is committed.
    
    Q1. Concurrency primitive — sync.Mutex (current lean), sync.RWMutex,
        or finer-grained per-bucket.
    Q2. Locking surface — lock on the existing cache struct, a separate
        sharedCache wrapper, or an opt-in ConcurrentSafe hook on the
        sqlite.Cache contract.
    Q3. Discovery — extend PageCache.Create, add PageCache.CreateShared,
        or detect at the binding level from the parent conn URI.
    
    A canonical TestSharedCacheTwoConns_Integrity (two connections with
    cache=shared, concurrent writes, PRAGMA integrity_check) is reserved
    in the file but not added until the locking shape is settled.
    
    Builds clean, gofmt clean, go vet introduces no new warnings beyond
    the pre-existing pool.go unsafe.Pointer notices on Buf/Extra. Existing
    pcache tests unchanged and green.
    
    References !127 review note "the assumption MR-C will need to revisit"
    and the original !126 description's deferred-to-MR-C scope.
    6250b755
  • Ian Chechin's avatar
    pcache: address !130 review round 2 · 606ef9c8
    Ian Chechin authored
    (1) Rework BenchmarkPoolEvictionChurn to use a rotating-residue
    DELETE (k % 3 = i % 3) with a matching-batch re-insert per cycle.
    Cycle i removes batchPerCycle rows from residue (i % 3) and
    re-inserts batchPerCycle rows back into the same residue in a
    fresh disjoint key range, so the next visit (three cycles later)
    finds rows to scan. Per-cycle work is constant from cycle 0
    onward: 200 deletes + 200 inserts + 1 incremental_vacuum, with
    the seed pre-populated as three 200-row partitions so cycle 0
    is already in steady state. easy-refusals/op and
    page-evictions/op are now rates that hold flat across benchtime
    values rather than a fixed first-cycle cost divided by b.N (was:
    5.36, 2.68, 1.34, 0.67 at 25x/50x/100x/200x; now: ~60.32
    throughout).
    
    (2) Tighten Stats.Evictions docstring to mention Unpin(discard
    =false) trimming back to target after a FetchCreateForce
    overcommit, per cznic's optional nit on round 1.
    
    (3) CHANGELOG: replace "merge request #N" placeholder with #130.
    606ef9c8
  • Ian Chechin's avatar
    sqlite: address !129 review (doc inversion + deterministic errstrForDB test) · 4b8c6a2c
    Ian Chechin authored
    Round-1 review follow-ups by cznic on !129 (#230):
    
    (1) Fix inverted boolean in the getErrorRcMode docstring. The
    "Absent parameter or true value preserves..." sentence said the
    opposite of what the code does and contradicted both the
    conn.errorRcMode field comment and the driver.go paragraph. Swap
    the true/false words so the doc agrees with the code.
    
    (2) Add TestErrstrForDBSuppressOnMismatch, a deterministic unit
    test that calls errstrForDB directly with a healthy db handle
    (sqlite3_extended_errcode = SQLITE_OK, sqlite3_errmsg = "not an
    error") and a deliberately mismatched rc = SQLITE_CANTOPEN. In
    legacy mode the formatter appends the stale "not an error" as the
    helpful detail; under errorRcMode=true the conditional suppress
    branch fires and the canonical errstr(rc) is used alone. Code()
    returns SQLITE_CANTOPEN in both modes. This pins the new behavior
    across SQLite versions and platforms independently of the
    host-specific ope...
    4b8c6a2c
  • cznic's avatar
    Merge branch 'error-rc-opt-in' into 'master' · 68fc1f41
    cznic authored
    sqlite: add _error_rc opt-in DSN parameter (#230)
    
    Closes #230
    
    See merge request !129
    68fc1f41
  • Ian Chechin's avatar
    pcache: per-cache mutex for -race cleanliness under cache=shared · 1580c89c
    Ian Chechin authored
    Resolves the question raised by cznic on !127 about what MR-C
    would need to revisit: the pool is already correct under SQLite's
    shared-cache mode, because every callback into a given Cache is
    serialised internally by sqlite3BtreeEnter on the BtShared mutex
    (verified empirically with a lock-free in-flight probe:
    max-in-flight = 1 on the canonical two-connection workload, 4 on
    a positive control with goroutines hitting the cache directly).
    The Go race detector, however, does not recognise SQLite's libc
    mutex as a happens-before edge and reports false-positive races
    on Fetch vs Unpin reads/writes of the per-cache state, which
    surfaces as DATA RACE failures for any user who registers the
    pool and runs their suite under -race.
    
    Take a sync.Mutex on the cache type on every public method
    (SetSize, PageCount, Fetch, Unpin, Rekey, Truncate, Destroy,
    Shrink), always. On the common non-shared-cache path the lock
    is uncontended (one atomic CAS per Lock/Unlock pair, negligible
    next to the SQLite work it bookends); on the shared-cache path
    it just rubber-stamps the order SQLite's BtShared mutex already
    established.
    
    Drop sharedCacheStub from sharing.go (cznic noted it triggers
    staticcheck U1000 and breaks make all). Rewrite sharing.go as a
    design record describing why the lock is always taken and the
    alternatives considered (always-taken vs conditional,
    document-as-unsupported vs reject-at-Create). The design-questions
    RFC scaffold is gone.
    
    Add TestSharedCacheTwoConns_Integrity in e2e_test.go: two sql.Conn
    against the same cache=shared URI with concurrent writers + PRAGMA
    integrity_check, runs cleanly under -race.
    
    CHANGELOG entry under TBC vNEXT.
    1580c89c
  • Ian Chechin's avatar
    pcache: update BenchmarkPoolEvictionChurn comment to reflect xRekey coverage · f49af948
    Ian Chechin authored
    The round-2 rotating-residue rework reliably triggers the b-tree
    rebalance paths that emit xRekey through the SQL surface
    (~13 Rekeys per cycle, 325 over 25 cycles, scaling linearly with
    b.N), so the benchmark now complements the dedicated xRekey unit
    tests (TestRekey, TestRekeyEvictsCollider) rather than deferring
    to them, contrary to what the existing comment claimed. Comment-only
    change per cznic's round-3 review on !130; TestPoolRoundTripIntegrity
    exercises a different (unchanged) workload and its comment stays
    accurate as-is.
    f49af948
  • cznic's avatar
    Add netbsd/amd64 support (#246) · 26443363
    cznic authored
    The committed lib/sqlite_netbsd_amd64.go was a stale old-generator transpile that
    no longer built (the mu.enter/mu.leave undefined break in issue #246) and was out
    of sync with the new-generator code every other platform uses. Re-transpile SQLite
    3.53.2 for netbsd/amd64 (generated on NetBSD 10.1 / Go 1.26.3) and re-vendor:
    
      - vendor_libs/main.go: add netbsd/amd64 to the libsqlite3 and libsqlite_vec
        target lists. Also skip emitting an un-prefixed type alias when it would
        collide with a const of the same name — netbsd's transpile emits spurious
        `const <typename> = 0` macro-eval artifacts (off_t, gid_t, ...) that otherwise
        redeclare the generated alias. No effect on the other platforms' output.
      - lib/sqlite_netbsd_amd64.go: regenerated (replaces the stale file).
      - vec/vec_netbsd_amd64.go: vendor sqlite-vec for netbsd; vec/patches.go and
        vec_test.go: include netbsd so the extension registers and is tested.
      - Makefile, builder.json: add netbsd/amd64...
    26443363
  • cznic's avatar
    go.mod: bump modernc.org/libc to v1.73.1 · 06815933
    cznic authored
    
    
    Picks up the netbsd/amd64 Xmmap PAD-ABI fix. modernc.org/sqlite#246.
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    06815933
  • cznic's avatar
    issue246-tracker.md: add NetBSD/amd64 status tracker · 7611e3ee
    cznic authored
    
    
    Document the issue #246 NetBSD/amd64 support status: Tier-2 done, the libc
    mmap PAD SIGBUS fix (v1.73.1), the cc/ccgo Tier-1 toolchain gate, the cascade
    landed on master, and the remaining maintainer tag cascade.
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    7611e3ee
  • cznic's avatar
    go.mod: bump modernc.org/libc to v1.73.3 · f1bccf88
    cznic authored
    
    
    Completes the netbsd/amd64 cascade — picks up the race-free netbsd Xabort (and
    the earlier mmap PAD fix). ABI-preserving for the vendored lib/vec and a no-op
    for non-netbsd targets (v1.73.1..v1.73.3 touch only libc_netbsd.go).
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    f1bccf88
  • cznic's avatar
  • cznic's avatar
    Re-vendor lib/ and vec/ from libsqlite3 v1.14.0 and libsqlite_vec v0.3.0 · e62c32f2
    cznic authored
    
    
    Regenerate the vendored transpiles from the now-released siblings (was generated
    from in-development trees during the netbsd/amd64 work). vec/* update to the
    v0.3.0 transpile; lib/sqlite_netbsd_amd64.go picks up the v1.14.0 netbsd diff
    (other targets unchanged — same SQLite version). build_all_targets passes;
    TestVec passes.
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    e62c32f2
  • cznic's avatar
    CHANGELOG: reframe netbsd/amd64 as experimental, not yet officially supported · e0fb13dd
    cznic authored
    
    
    The revived port has green CI across the chain but zero production mileage, so
    it stays out of the supported-platforms list in doc.go pending broader real-world
    testing (~a month). Expand the note with the libc mmap-PAD and abort(3) fixes and
    the call for users to evaluate it and report via #246.
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    e0fb13dd
  • cznic's avatar
    lib, vec: deduplicate generated sources (-2.9M lines, 174 -> 64 MB) · acabf564
    cznic authored
    The per-target transpiles in lib/ (SQLite) and vec/ (sqlite-vec) ship one
    generated Go file per GOOS/GOARCH. Declarations byte-identical across targets are
    now folded into build-tagged shared files -- lib/sqlite.go + lib/sqlite_g_<hex>.go
    and vec/vec.go + vec/vec_g_<hex>.go -- by modernc.org/undup, wired into
    `make vendor`.
    
    This is a packaging change only. Go's build constraints make every target compile
    exactly the same declarations as before; the public API and behavior are
    unchanged, `make build_all_targets` is green on all ~20 platforms, and the suite
    (incl. TestVec) passes. Hand-written platform files (libsqlite3_*.go, hooks_*.go,
    ...) carry no generated-code marker and are untouched.
    
        421 files changed, 1,523,713 insertions(+), 4,437,082 deletions(-)
        lib  164.8 -> 62.2 MB (2.65x)
        vec    8.6 ->  2.1 MB (4.19x)
    
    Net ~2.9M fewer lines of vendored generated code -- good news for clone, fetch,
    and build times. The motivation...
    acabf564
  • cznic's avatar
    CHANGELOG.md: consolidate untagged v1.53.0/v1.54.0 into one v1.53.0 section · 1897fdd6
    cznic authored
    
    
    The latest real tag is v1.52.0; v1.53.0 and v1.54.0 existed only as
    CHANGELOG sections, splitting one pending release across two version
    numbers. Renumber the premature v1.54.0 header to v1.53.0 and fuse the
    two sections so the next tag is honestly v1.53.0. No released (tagged)
    section is touched.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    1897fdd6
  • cznic's avatar
    Merge branch 'pcache-pool-polish' into 'master' · 73050dce
    cznic authored
    pcache: address !127 review follow-ups (Stats accuracy + EasyRefusals counter)
    
    See merge request !130
    73050dce
  • cznic's avatar
    Add freebsd/386 + freebsd/arm targets · 8725c222
    cznic authored
    
    
    Vendor fresh SQLite 3.53.2 + sqlite-vec transpiles for freebsd/386 and
    freebsd/arm (replacing the stale 3.41 freebsd_386 file) via make vendor, and
    enable them in vendor_libs, build_all_targets, and builder.json test.
    
    Requires modernc.org/libc v1.73.4: with the previous libc, freebsd/arm's WAL
    shared-memory mmap faulted (SIGBUS) because the 64-bit off_t was mis-encoded for
    32-bit; v1.73.4 fixes the per-arch mmap off_t encoding. Runtime-tested on both
    freebsd/386 and freebsd/arm (core + WAL/concurrency + vec). doc.go's
    supported-targets list is left for a follow-up after broader testing, mirroring
    how netbsd/amd64 was introduced.
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    8725c222
  • cznic's avatar
    vendor: regenerate freebsd/arm vec at SQLite 3.53.2 · 14e5790e
    cznic authored
    
    
    The freebsd/arm sqlite-vec transpile was generated against the older libsqlite3
    (SQLite 3.53.1) while the rest of the tree is 3.53.2; re-vendored from
    libsqlite_vec regenerated at 3.53.2 so all targets are consistent. Functionally
    unchanged (3.53.1->3.53.2 is a patch, no ABI change; freebsd/386+arm runtime
    tests already passed).
    
    Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
    14e5790e
  • cznic's avatar
    Merge branch 'pcache-shared-cache-draft' into 'master' · adff4b17
    cznic authored
    pcache: per-cache mutex for -race cleanliness under cache=shared
    
    See merge request !131
    adff4b17
  • cznic's avatar
  • Ian Chechin's avatar
    sqlite: add DBStatus wrapper for sqlite3_db_status + pcache spill-I/O benchmark · 40ff0274
    Ian Chechin authored
    Adds a Go binding for sqlite3_db_status, the per-connection runtime
    counters (cache hit/miss/write/spill, schema/statement memory,
    lookaside usage, deferred FKs), as discussed on the !130 review.
    
    dbstatus.go: DBStatus interface implemented by *conn and reached via
    (*sql.Conn).Raw(), mirroring the FileControl surface cznic pointed at.
    DBStatusOp is a distinct typed enum of the SQLITE_DBSTATUS_* verbs so a
    constant from another op family will not compile in its place; all 14
    ops the transpiled lib defines are exposed. Status(op, reset) returns
    the (current, high) pair via the tls.Alloc(8) two-int32 pattern from
    cznic's skeleton and surfaces an out-of-range op as an error.
    
    dbstatus_test.go: exercises the three counter families through Raw() -
    SchemaUsed (memory high-water) grows after DDL; CacheHit (running
    counter) resets; LookasideHit reports its value in high not current;
    an out-of-range op errors.
    
    pcache/bench_test.go: Ben...
    40ff0274
  • cznic's avatar
    sqlite: review fixes for !132 — restore #131 CHANGELOG link, correct DBStatus op-family docs · 759639fa
    cznic authored
    
    
    - CHANGELOG.md: re-add the "See merge request #131" line that the MR diff
      dropped, so the pcache -race-clean entry keeps its attribution instead of
      reading as part of !132.
    - dbstatus.go: make the op-family doc match the transpiled
      sqlite3_db_status64 behavior — only DBStatusLookasideUsed maintains a
      high-water mark; CacheUsed/SchemaUsed/StmtUsed/CacheUsedShared report
      high==0 with the reset flag ignored; DBStatusDeferredFKs is a 0/1 flag
      (reset ignored); DBStatusTempbufSpill is a running byte counter (was
      undocumented). No code/behavior change.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    759639fa
  • cznic's avatar
    Merge branch 'dbstatus-binding' into 'master' · 697300ff
    cznic authored
    sqlite: add DBStatus wrapper for sqlite3_db_status
    
    See merge request !132
    697300ff
  • cznic's avatar
    CHANGELOG.md: document experimental freebsd/386 + freebsd/arm (#119) · 6b32d1ee
    cznic authored
    
    
    Pre-v1.53.0 release prep; documentation and module hygiene only, no
    change to any compiled source:
    
    - CHANGELOG.md: add an experimental-status entry for freebsd/386 and
      freebsd/arm (MR #119, Olivier Cochard-Labbé / @ocochard), mirroring
      the netbsd/amd64 framing -- shipped and CI-tested but intentionally
      not yet in doc.go's supported-platforms list, pending broader
      real-world testing. Also bump the v1.53.0 entry date to the tag date.
    - CLAUDE.md: correct the stale "transpiled SQLite 3.53.1" to 3.53.2.
    - go.sum: go mod tidy, pruning orphan toolchain hashes; go.mod and all
      selected module versions are unchanged.
    - Remove issue246-tracker.md, an internal status tracker not meant to
      ship in the released module.
    
    Co-Authored-By: default avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
    6b32d1ee
Loading
Loading