You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,8 @@ Plain `database/sql` works too: `sql.Open("sqlite", "file:app.db")`. The full se
141
141
142
142
Full per-package recipes, the `_*` DSN-flag table, and build-tag mapping: **[Migrating](docs/guides/migrating.md)**, **[DSN flags](docs/reference/dsn-flags.md)**, **[Build tags](docs/reference/build-tags.md)**. Runnable: [`examples/migrating/`](examples/migrating/).
143
143
144
+
The drop-in claim is **CI-enforced**: every push runs `gorm.io/gorm`'s full integration suite, plus vendored subsets of `mattn/go-sqlite3`'s and `modernc.org/sqlite`'s own test suites, against this module (with an xorm-compatibility lane alongside) — so compatibility is checked by tests upstream wrote, not by tests we wrote to flatter ourselves. Recipes and divergence tables: [`dev/upstream/`](dev/upstream/).
145
+
144
146
## Why CGo-free
145
147
146
148
Because SQLite is transpiled to Go (via `modernc.org/sqlite`) rather than C-bound, you get: builds in `golang:alpine` / distroless with no `apk add`; `GOOS=… GOARCH=… go build` cross-compilation that just works; clean `go test -race`; reproducible builds with no vendored amalgamation; and CI on providers that disallow CGo. The cost is a constant-factor gap on hot UDF/per-row paths — invisible for most applications. More in [Getting started](docs/getting-started.md#why-cgo-free) and [Performance](docs/reference/performance.md).
Copy file name to clipboardExpand all lines: docs/guides/custom-vfs.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,13 +19,15 @@ db.Close()
19
19
vfs.Unregister("myvfs") // after every db against it is closed
20
20
```
21
21
22
-
Implement [`vfs.VFS`](https://pkg.go.dev/gosqlite.org/vfs#VFS) (`Open`/`Delete`/`Access`/`FullPathname`) and [`vfs.File`](https://pkg.go.dev/gosqlite.org/vfs#File) (`ReadAt`/`WriteAt`/`Truncate`/`Sync`/`Size`/locking/`Close`). Embed `vfs.NoLock` to satisfy the advisory-lock trio with accept-everything semantics (correct for single-process backends). Return a `vfs.VFSError` from any method to surface a specific `SQLITE_*` result code; a plain error becomes `SQLITE_IOERR`.
22
+
Implement [`vfs.VFS`](https://pkg.go.dev/gosqlite.org/vfs#VFS) (`Open`/`Delete`/`Access`/`FullPathname`) and [`vfs.File`](https://pkg.go.dev/gosqlite.org/vfs#File) (`ReadAt`/`WriteAt`/`Truncate`/`Sync`/`Size`/locking/`Close`). Embed `vfs.NoLock` to satisfy the advisory-lock trio with accept-everything semantics — correct only for **single-connection** access (multiple connections in WAL mode need real locking; see [WAL](#wal--the-shmfile-capability)). Return a `vfs.VFSError` from any method to surface a specific `SQLITE_*` result code; a plain error becomes `SQLITE_IOERR`.
23
23
24
24
A complete ~80-line in-memory backend is at [`examples/features/vfs/custom/`](../../examples/features/vfs/custom/main.go).
25
25
26
26
## WAL — the ShmFile capability
27
27
28
-
A custom VFS runs in rollback-journal mode by default. To unlock WAL, have your `File` also implement `vfs.ShmFile` — a single `ShmGroup() string` method declaring which open files share a WAL index. The dispatcher owns the shared memory and the 8-slot WAL lock table, so you never touch unsafe memory or the lock protocol. WAL coordination is in-process (it backs multiple `database/sql` connections to one Go-managed database within a process, not cross-process WAL over a real disk).
28
+
A custom VFS runs in rollback-journal mode by default. To unlock WAL, have your `File` also implement `vfs.ShmFile` — a single `ShmGroup() string` method declaring which open files share a WAL index. The dispatcher owns the shared memory and the 8-slot WAL lock table, so you never touch unsafe memory or the shared-memory lock protocol. WAL coordination is in-process (it backs multiple `database/sql` connections to one Go-managed database within a process, not cross-process WAL over a real disk).
29
+
30
+
> **Multi-connection WAL needs real db-file locking — do not embed `vfs.NoLock`.** The dispatcher arbitrates the WAL *shared-memory* locks, but SQLite still gates destructive operations — notably the checkpoint it runs when a connection closes, which resets the `-wal` — on first acquiring an EXCLUSIVE *db-file* lock. `vfs.NoLock` grants that EXCLUSIVE even while other connections are active, so the close-checkpoint can reset the WAL under a concurrent writer and corrupt the database. Implement real `Lock` / `Unlock` / `CheckReservedLock` on the main db file: many connections may share `LockShared`; `LockExclusive` must fail while any other connection holds `LockShared`. The reference `File` in the `vfs` package tests is a complete in-process example.
Copy file name to clipboardExpand all lines: skills/custom-vfs/SKILL.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,13 +34,15 @@ type File interface {
34
34
}
35
35
```
36
36
37
-
-**Embed `vfs.NoLock`** in your File to get the lock trio for free (correct for single-process backends).
37
+
-**Embed `vfs.NoLock`** in your File to get the lock trio for free — correct only for **single-connection** access (see the WAL caveat below; multi-connection WAL needs real locking).
38
38
-**Errors:** return `&vfs.VFSError{Code: sqlite3.SQLITE_READONLY}` (or any `SQLITE_*`) for a specific code; a plain error becomes `SQLITE_IOERR`. A short read past EOF returns `io.EOF` (dispatcher zero-fills + reports SHORT_READ).
39
39
-**Buffers are copied at the boundary** — a `File.ReadAt` is handed a fresh slice; don't alias it past the call.
40
40
41
41
## WAL
42
42
43
-
Default is rollback-journal. For WAL, also implement `vfs.ShmFile` (one method, `ShmGroup() string`, declaring which files share a WAL index). The dispatcher owns the shared memory + lock table. In-process only.
43
+
Default is rollback-journal. For WAL, also implement `vfs.ShmFile` (one method, `ShmGroup() string`, declaring which files share a WAL index). The dispatcher owns the shared memory + WAL lock table. In-process only.
44
+
45
+
> **Multi-connection WAL needs real db-file locking — do NOT embed `vfs.NoLock`.** The dispatcher arbitrates the WAL *shared-memory* locks, but SQLite still gates destructive operations — notably the checkpoint it runs when a connection closes, which resets the `-wal` — on first acquiring an EXCLUSIVE *db-file* lock. `NoLock` hands out that EXCLUSIVE even while other connections are active, so the close-checkpoint can reset the WAL under a concurrent writer and corrupt the database. Implement real `Lock` / `Unlock` / `CheckReservedLock` on the main db file: many holders may share `LockShared`; `LockExclusive` must fail while any other connection holds `LockShared`. The reference `File` in the `vfs` package tests shows the full pattern.
0 commit comments