gosuda.org/sqlite3: En drop-in database/sql-driver som kjører innebygd C SQLite over delt minne
gosuda.org/sqlite3 er en CGO_ENABLED=0 database/sql-driver for Go som kjører den overordnede C SQLite 3.54.1-amalgameringen inne i en isolert arbeiderprosess over HQQ delt-minne-ringer. Den registreres under standarddrivernavnet "sqlite3", støtter DSN-spørringsparametere for github.com/mattn/go-sqlite3, innebygger SLSA-attesterte forhåndsbygde serverbinærfiler for seks operativsystemer og arkitekturmål, og bruker forespørsels-pipelining med 64 KiB satsvis forhåndshenting for å overgå in-prosess cgo på spørringer med flere rader.
1go get gosuda.org/sqlite3@v0.3.3540100
Hvorfor en ny Go SQLite-driver?
SQLite er en av de mest distribuerte programvarekomponentene i verden, men å bruke den fra Go har historisk sett tvunget ingeniører inn i et ukomfortabelt valg mellom byggeenkelhet og motorlojalitet.
På den ene siden står github.com/mattn/go-sqlite3, den langvarige standarden i fellesskapet. Den pakker inn den offisielle C SQLite-amalgameringen direkte via cgo. Du får den ekte C-spørringsplanleggeren, full WAL-sameksistenssemantikk og forutsigbar C-minnehåndtering. Kostnaden er CGO_ENABLED=1: lokale bygg og CI-pipeliner sakter ned, minimale scratch- eller distroless-beholderavbildninger krever matchende C-kjøretidsbiblioteker, krysskompilering til et annet operativsystem eller CPU-arkitektur krever en fullstendig C-krysseverktøykjede, og enhver minnekorrupsjon i C deler Go-prosessens adresseområde.
På den andre siden finnes ren-Go SQLite-porter generert ved mekanisk å oversette C-amalgameringen til Go-kildekode over en emulert C-kjøretid (libc). De gjenoppretter CGO_ENABLED=0, noe som gjør bygging trivielt. Under vedvarende arbeidsmengder øker imidlertid den oversatte C-hopen og de radvise Go-allokeringene søppelinnsamlingstrykket, og subtile grensetilfeller innen låsing, opptatt-tidsavbrudd eller setningsavbrudd kan avvike fra native C-bygg.
Vi bygget gosuda.org/sqlite3 slik at Go-applikasjoner ikke behøver å velge mellom CGO_ENABLED=0 og den ekte C SQLite-motoren. Ved å kompilere den offisielle SQLite 3.54.1 C-amalgameringen til en dedikert delt-minne-server (hqq-sqlite-server) og kommunisere over gosuda.org/sqlite3, kompilerer din Go-binærfil med CGO_ENABLED=0 samtidig som den utfører umodifisert, -O3-optimalisert C SQLite på tvers av en prosessgrense.
Null-konfigurasjonsutførelse: Fra go get til sql.Open
Å erstatte github.com/mattn/go-sqlite3 i en eksisterende Go-kildebase krever at man kun endrer den blanke importeringsstien. Driveren registreres som "sqlite3" (og "hqq-sqlite" for eksplisitt valg) og aksepterer de samme DSN-spørringsstrengene:
1package main
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "log"
8
9 _ "gosuda.org/sqlite3"
10)
11
12func main() {
13 dsn := "file:app.db?_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate&_foreign_keys=on"
14 db, err := sql.Open("sqlite3", dsn)
15 if err != nil {
16 log.Fatal(err)
17 }
18 defer db.Close()
19
20 ctx := context.Background()
21 if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)`); err != nil {
22 log.Fatal(err)
23 }
24 if _, err := db.ExecContext(ctx, `INSERT INTO users (name) VALUES (?)`, "Ada Lovelace"); err != nil {
25 log.Fatal(err)
26 }
27
28 var id int64
29 var name string
30 if err := db.QueryRowContext(ctx, `SELECT id, name FROM users WHERE name = ?`, "Ada Lovelace").Scan(&id, &name); err != nil {
31 log.Fatal(err)
32 }
33 fmt.Printf("user #%d: %s\n", id, name)
34}
Innebygde SLSA-attesterte arbeiderbinærfiler
Et naturlig spørsmål når man hører at et bibliotek bruker en C-arbeiderprosess, er: "Må jeg installere eller distribuere en separat C-binærfil sammen med mitt Go-program?"
Nei. gosuda.org/sqlite3 innebygger forhåndsbygde hqq-sqlite-server-binærfiler for alle seks store mål direkte inne i plattformtaggede Go-filer (internal/embedded/embed_<goos>_<goarch>.go). Fordi Gos byggerestriksjoner kun kompilerer filen som samsvarer med din målsatte GOOS og GOARCH, vil krysskompilering av GOOS=linux GOARCH=arm64 go build automatisk bygge inn kun den omtrent 1,4 MiB store linux/arm64-serverbinærfilen.
| Mål-operativsystem | Målarkitektur (GOARCH) | Innebygd binærfil | Byggeverktøykjede og proveniens |
|---|---|---|---|
| Linux | amd64, arm64 | hqq-sqlite-server_linux_<arch> | GCC -O3, SLSA Build L3 Provenance |
| macOS | arm64, amd64 | hqq-sqlite-server_darwin_<arch> | AppleClang -O3, SLSA Build L3 Provenance |
| Windows | amd64 | hqq-sqlite-server_windows_amd64.exe | MSVC -O2, SLSA Build L3 Provenance |
| Windows | arm64 | hqq-sqlite-server_windows_arm64.exe | zig cc -target aarch64-windows-gnu -O3, SLSA L3 |
Når sql.Open etablerer sin første tilkobling, materialiserer autolaunch.go den innebygde binærfilen til brukerens buffermappe ($XDG_CACHE_HOME/hqq/bin/<sha256>/), verifiserer dens kryptografiske SHA-256-sammendrag mot det festede ReleaseHashes-kartet i hashes.go via gosuda.org/hqq/secureexec, og starter arbeideren med --parent-stdin slik at levetiden er strengt bundet til din Go-prosess.
Rentroms mattn/go-sqlite3-DSN-kompatibilitet
For å fungere som en ekte erstatning rett ut av esken implementerer DSN-parseren (dsn.go) en rentromsreplika av tilkoblings- og pragma-bryterne som brukes på tvers av mattn/go-sqlite3-kildebaser:
| DSN-spørringsparameter | Aksepterte verdier | SQLite-effekt |
|---|---|---|
_busy_timeout / _timeout | Heltalls millisekunder (f.eks. 5000) | Kaller sqlite3_busy_timeout(db, ms) ved øktåpning. |
_journal_mode / _journal | DELETE, TRUNCATE, PERSIST, MEMORY, WAL, OFF | Utfører PRAGMA journal_mode = <mode>. |
_txlock | immediate, exclusive, deferred | Velger BEGIN IMMEDIATE, BEGIN EXCLUSIVE eller BEGIN DEFERRED for db.BeginTx. |
_foreign_keys / _fk | on, true, 1, off, false, 0 | Utfører PRAGMA foreign_keys = ON/OFF. |
_synchronous / _sync | 0/OFF, 1/NORMAL, 2/FULL, 3/EXTRA | Utfører PRAGMA synchronous = <level>. |
_cache_size | Heltallssider eller -KiB (f.eks. -20000) | Utfører PRAGMA cache_size = <val>. |
_loc | auto eller IANA-tidssone (f.eks. UTC, Asia/Seoul) | Parser DATETIME/TIMESTAMP-tekstkolonner til *time.Time i målokasjonen. |
mode | ro, rw, rwc, memory | Setter SQLITE_OPEN_READONLY, SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE eller SQLITE_OPEN_MEMORY. |
_case_sensitive_like, _query_only, _recursive_triggers, _secure_delete, _AutoVacuum | Booleanske verdier eller heltallsmoduser | Bruker den tilhørende øktens PRAGMA ved tilkoblingsinitialisering. |
Gjøre prosessisoleringsfordeler om til gjennomgyningsgevinst
Ved første øyekast virker det som om det å kjøre SQLite i en separat prosess må være tregere enn å kalle sqlite3_step i-prosess via cgo. I praksis skjer det motsatte så snart en spørring returnerer mer enn en enkelt rad.
Den skjulte kostnaden ved radvise cgo-kall
I en konvensjonell cgo SQLite-driver driver database/sql iterering én rad og én kolonne av gangen. Å lese 1 000 rader med 4 kolonner utløser:
- 1 000
cgo-kall tilsqlite3_step - 4 000
cgo-kall tilsqlite3_column_typeogsqlite3_column_int64/sqlite3_column_text - Tusenvis av individuelle grensekryssinger mellom Go-planleggeren og C-stakken
Forespørsels-pipelining og 64 KiB satsvis forhåndshenting
gosuda.org/sqlite3 eliminerer radvise grensekryssinger ved å omdesigne trådløsprotokollen rundt to mekanismer i stmt.go, rows.go og sqlite3/server/sqlite3_hooks.c:
1Go-klient (stmt.go / rows.go) C11-arbeider (sqlite3_hooks.c)
2----------------------------- ----------------------------
31. Pipeliner i 1 operasjon:
4 ResetAsync + BindValuesAsync
5 + StmtInfoAsync + FetchRowsAsync(128) =======> reset -> bind -> stmt_info ->
6 løkke sqlite3_step() opp til 128 rader
7 <======= Sats 1 (rader 1..128 pakket i 64 KiB-plass)
82. Utløs forhåndshenting umiddelbart:
9 FetchRowsAsync(128) (Sats 2) =======> løkke sqlite3_step() for rader 129..256
10 Mens C utfører trinn for Sats 2, skanner
11 Go-kalleren rader 1..128 fra Sats 1! <======= Sats 2 klar i delt minne
- Enkeltoperasjons-spørrings-pipelining (
stmt.go): NårQueryContextellerStmt.QueryContextkjører en skrivebeskyttet setning, senders.query()innreset,bind_values,stmt_info(ved første utførelse) ogfetch_rowsasynkront i én enkelt delt-minne-operasjon. Alle fire operasjonene utføres etter hverandre på C-arbeideren og returnerer i én rundtur. - Tette C-satsløkker og asynkron forhåndshenting (
rows.go): Inne ifetch_rows(sqlite3_hooks.c) utfører C-arbeideren trinnvisesqlite3_step(stmt)i en native C-løkke, og koder opp tilFetchSize-rader (standard 128) direkte inn i svarsplassen for delt minne på 64 KiB til satsgrensen er nådd, plassen er full, ellerSQLITE_DONEoppstår. Så snartrows.ingest()dekoder Sats 1, utløser denFetchRowsAsyncfor Sats 2 før den returnerer den første raden til kalleren. Mens Go-koden din skanner Sats 1, utfører C-arbeideren allerede trinn for Sats 2 på en annen CPU-kjernen.
På Apple M4 (darwin/arm64) viser en sammenligning av gosuda.org/sqlite3 (CGO_ENABLED=0) mot github.com/mattn/go-sqlite3 (CGO_ENABLED=1) på den samme SQLite-databasen effekten av satsvis behandling og pipelining:
| Arbeidsmengde | gosuda.org/sqlite3 (CGO_ENABLED=0) | mattn/go-sqlite3 (CGO_ENABLED=1) | Relativ hastighet |
|---|---|---|---|
Punktutvalg (QueryRow med primærnøkkel) | 3.52 µs/op (9 B, 1 allokering) | 3.56 µs/op (232 B, 11 allokeringer) | 1,01x (paritet, 11x færre allokeringer) |
100-raders skanning (rows.Next) | 20.41 µs/op (30.5 KB, 211 allokeringer) | 36.34 µs/op (21.8 KB, 309 allokeringer) | 1,78x raskere |
1 000-raders skanning (rows.Next) | 125.92 µs/op (296 KB, 2 030 allokeringer) | 349.10 µs/op (216 KB, 3 009 allokeringer) | 2,77x raskere |
| Forberedt innsetting (Autocommit) | 5.33 µs/op (0 B, 0 allokeringer) | 5.88 µs/op (120 B, 5 allokeringer) | 1,10x raskere (0 allokeringer) |
| 100-raders transaksjonssats | 189.99 µs/op (16 B, 1 allokering) | 191.50 µs/op (12.1 KB, 503 allokeringer) | 1,01x (500x færre allokeringer) |
Avbrudd, stisandkasse og gjenoppretting av tilkoblingsbasseng
Å kjøre SQLite i en isolert arbeider styrker også hvordan driveren håndterer tidsavbrudd, ubetrodd SQL og filsystemgrenser:
- Deterministisk kontekstavbrudd (
conn.googerrors.go): Hvert driverkall aktiverer en lettvektscontext.AfterFunc-vakt (cn.guard). Hvis kallerenscontext.Contextavbrytes eller tidsfristen utløper mens en langsom rekursiv spørring eller låsevente kjører på serveren, markerer vakten tilkoblingen som feilaktig, avslutter den lokale øktkoblingen og sender en utenom-båndetCancel()-ramme. C-arbeideren anropersqlite3_interrupt(db)umiddelbart, og frigjørsqlite3_step, mens Go-driveren tilordner den resulterendeSQLITE_INTERRUPT-statusen tilcontext.Canceledellercontext.DeadlineExceededog instruererdatabase/sqltil å erstatte tilkoblingen i bassenget. - Katalogsandkasse (
sqlsrv_pathrules.c): C-arbeideren håndhever--allow-dir-stiregler ved hvertopen_v2-kall, oppløser symbolsk lenker og avviser relativ..-navigasjon utenfor den tillatte katalogen.sqlite3_load_extensionogSQLITE_LIMIT_ATTACHEDer deaktivert som standard slik at SQL-injeksjon ikke kan unnslippe databankatalogen eller laste inn vilkårlige delte objekter.
Arkivstruktur
Arkivet er strukturert slik at toppnivåpakken tilbyr database/sql-driveren rett ut av esken, mens launcher/ og sqlite3/ forblir tilgjengelige for avanserte tilpassede serverdistribusjoner:
1gosuda.org/sqlite3
2├── driver.go / api.go # database/sql-driverregistrering ("sqlite3") og Connector-API
3├── dsn.go # Rentroms mattn/go-sqlite3 DSN-parser og pragma-konfigurasjon
4├── conn.go / tx.go # Øktlevetid, kontekstvakt og transaksjonstilstandsmaskin
5├── stmt.go / rows.go # Enkeltoperasjons-spørrings-pipelining og 64 KiB satsvis forhåndshenting
6├── errors.go # Typede SQLite primære/utvidede feilkoder og sentinel-samsvar
7├── autolaunch.go / hashes.go # Null-konfigurasjons innebygd servermaterialisering og SHA-256-festing
8├── internal/embedded/ # Plattformtaggede //go:embed-binærfiler (linux, darwin, windows)
9├── launcher/ # Konfigurerbar prosessovervåker for tilpassede serverdistribusjoner
10└── sqlite3/ # Generert hqq-bindgen Go-klient og C11 SQLite-serverkilde
11 └── server/ # C11-serverimplementering (sqlite3_hooks.c, sqlsrv_policy.c)
Kom i gang
Installer modulen med go get og åpne databasen din med sql.Open("sqlite3", dsn):
1go get gosuda.org/sqlite3@v0.3.3540100
- GitHub-arkiv: https://github.com/gosuda/hqq-sqlite3
- DSN- og Pragma-referanse: docs/DSN.md
- Arkitektur og trådløsprotokoll: docs/ARCHITECTURE.md