---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql-drivrutin över delat minne för IPC'
description: Drop-in-databasdrivrutin för database/sql för SQLite 3.54.1 i Go med CGO_ENABLED=0, HQQ-arbetarisolering i delat minne, förhämtning av batchrader om 64 KiB och SLSA-attesterade binärfiler.
language: sv
date: 2026-10-18T14:02:16.803040007Z
path: /sqlite3
go_package: gosuda.org/sqlite3
go_repourl: https://github.com/gosuda/hqq-sqlite3.git
category: database
---

`gosuda.org/sqlite3` är en `database/sql`-drivrutin för Go med `CGO_ENABLED=0` som kör den upstream C SQLite `3.54.1`-amalgamationen i en isolerad arbetsprocess över [HQQ](https://gosuda.org/hqq)-delade minnesringar. Den registreras under det standardiserade drivrutinsnamnet `"sqlite3"`, stöder DSN-frågeparametrar från `github.com/mattn/go-sqlite3`, bäddar in SLSA-attesterade förkompilerade serverbinärer för sex operativsystem- och arkitekturmål, och använder begärandepipelining med 64 KiB batchförhämtning för att överträffa in-process `cgo` vid frågor som returnerar flera rader.

```bash
go get gosuda.org/sqlite3@v0.3.3540100
```

## Varför ytterligare en SQLite-drivrutin för Go?

SQLite är en av de mest utbredda programvarorna i världen, men att använda den från Go har historiskt tvingat ingenjörer att välja mellan byggenkelhet och motortrohet.

På ena sidan står `github.com/mattn/go-sqlite3`, den långvariga gemenskapsstandarden. Den omsluter den officiella C SQLite-amalgamationen direkt via `cgo`. Man får den verkliga C-frågeplaneraren, fullständig WAL-samtidighetssemantik och förutsägbar C-minneshantering. Kostnaden är `CGO_ENABLED=1`: lokala byggen och CI-pipelines blir långsammare, minimala scratch- eller distroless-containeravbildningar kräver motsvarande C-körbibliotek, korskompilering till ett annat operativsystem eller en annan CPU-arkitektur kräver en fullständig C-korsverktygskedja, och all minneskorruption i C delar adressrymden med Go-processen.

På andra sidan finns rena Go-portningar av SQLite, genererade genom mekanisk översättning av C-amalgamationen till Go-källkod över en emulerad C-körmiljö (`libc`). De återställer `CGO_ENABLED=0`, vilket gör byggen trivialt. Under långvarig belastning ökar dock den översatta C-heapen och de allokeringar per rad i Go trycket på skräpinsamlingen, och subtila kantfall i låsning, timeouts för upptagen resurs eller avbrytande av satser kan avvika från inbyggda C-byggen.

Vi byggde `gosuda.org/sqlite3` så att Go-applikationer inte behöver välja mellan `CGO_ENABLED=0` och den verkliga C-SQLite-motorn. Genom att kompilera den officiella SQLite `3.54.1` C-amalgamationen till en dedicerad server med delat minne (`hqq-sqlite-server`) och kommunicera via `gosuda.org/hqq`, kompileras din Go-binär med `CGO_ENABLED=0` medan oförändrad, `-O3`-optimerad C SQLite exekveras över en processgräns.

## Konfigurationsfri exekvering: från `go get` till `sql.Open`

Att ersätta `github.com/mattn/go-sqlite3` i en befintlig Go-kodbas kräver endast att den tomma importsökvägen ändras. Drivrutinen registreras som `"sqlite3"` (och `"hqq-sqlite"` för explicit val) och accepterar samma DSN-frågesträngar:

```go
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	_ "gosuda.org/sqlite3"
)

func main() {
	dsn := "file:app.db?_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate&_foreign_keys=on"
	db, err := sql.Open("sqlite3", dsn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	ctx := context.Background()
	if _, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)`); err != nil {
		log.Fatal(err)
	}
	if _, err := db.ExecContext(ctx, `INSERT INTO users (name) VALUES (?)`, "Ada Lovelace"); err != nil {
		log.Fatal(err)
	}

	var id int64
	var name string
	if err := db.QueryRowContext(ctx, `SELECT id, name FROM users WHERE name = ?`, "Ada Lovelace").Scan(&id, &name); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("user #%d: %s\n", id, name)
}
```

### Inbäddade SLSA-attesterade arbetsbinärer

En naturlig fråga när man hör att ett bibliotek använder en C-arbetsprocess är: _"Måste jag installera eller distribuera en separat C-binär tillsammans med mitt Go-program?"_

Nej. `gosuda.org/sqlite3` bäddar in förkompilerade `hqq-sqlite-server`-binärer för alla sex större mål direkt i plattformsmärkta Go-filer (`internal/embedded/embed_<goos>_<goarch>.go`). Eftersom Go:s byggvillkor endast kompilerar filen som motsvarar ditt mål `GOOS` och `GOARCH`, bäddar korskompilering med `GOOS=linux GOARCH=arm64 go build` automatiskt in endast den cirka 1,4 MiB stora serverbinären för `linux/arm64`.

| Måloperativsystem | Målarkitektur (`GOARCH`) | Inbäddad binär                        | Byggverktygskedja och proveniens                  |
| :---------------- | :----------------------- | :------------------------------------ | :------------------------------------------------ |
| **Linux**         | `amd64`, `arm64`         | `hqq-sqlite-server_linux_<arch>`      | GCC `-O3`, SLSA Build L3-proveniens               |
| **macOS**         | `arm64`, `amd64`         | `hqq-sqlite-server_darwin_<arch>`     | AppleClang `-O3`, SLSA Build L3-proveniens        |
| **Windows**       | `amd64`                  | `hqq-sqlite-server_windows_amd64.exe` | MSVC `-O2`, SLSA Build L3-proveniens              |
| **Windows**       | `arm64`                  | `hqq-sqlite-server_windows_arm64.exe` | `zig cc -target aarch64-windows-gnu -O3`, SLSA L3 |

När `sql.Open` upprättar sin första anslutning materialiserar `autolaunch.go` den inbäddade binären i användarens cachekatalog (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifierar dess kryptografiska SHA-256-digest mot den fastlåsta `ReleaseHashes`-mappen i `hashes.go` via `gosuda.org/hqq/secureexec`, och startar arbetsprocessen med `--parent-stdin` så att dess livslängd strikt är bunden till din Go-process.

### Ren-rums-kompatibilitet med DSN från `mattn/go-sqlite3`

För att fungera som en verklig drop-in-ersättning implementerar DSN-parsern (`dsn.go`) en ren-rums-replik av anslutnings- och pragma-inställningarna som används i kodbaser för `mattn/go-sqlite3`:

| DSN-frågeparameter                                                                            | Accepterade värden                                      | SQLite-effekt                                                                                         |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :---------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Heltal i millisekunder (t.ex. `5000`)                   | Anropar `sqlite3_busy_timeout(db, ms)` vid sessionsöppning.                                           |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Kör `PRAGMA journal_mode = <mode>`.                                                                   |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Väljer `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` eller `BEGIN DEFERRED` för `db.BeginTx`.                  |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Kör `PRAGMA foreign_keys = ON/OFF`.                                                                   |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Kör `PRAGMA synchronous = <level>`.                                                                   |
| `_cache_size`                                                                                 | Heltal i sidor eller `-KiB` (t.ex. `-20000`)            | Kör `PRAGMA cache_size = <val>`.                                                                      |
| `_loc`                                                                                        | `auto` eller IANA-tidszon (t.ex. `UTC`, `Asia/Seoul`)   | Tolkar `DATETIME`/`TIMESTAMP`-textkolumner som `*time.Time` i målplatsen.                             |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Sätter `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` eller `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Booleskt värde eller lägesheltal                        | Tillämpar motsvarande sessions-`PRAGMA` vid initiering av anslutningen.                               |

## Att förvandla processisolering till en genomströmningsfördel

Vid första anblicken verkar det som att köra SQLite i en separat process måste vara långsammare än att anropa `sqlite3_step` in-process via `cgo`. I praktiken inträffar motsatsen så snart en fråga returnerar mer än en enda rad.

### Den dolda kostnaden för rad-för-rad-anrop via `cgo`

I en konventionell `cgo`-drivrutin för SQLite driver `database/sql` iterationen en rad och en kolumn i taget. Läsning av 1 000 rader med 4 kolumner utlöser:

- 1 000 `cgo`-anrop till `sqlite3_step`
- 4 000 `cgo`-anrop till `sqlite3_column_type` och `sqlite3_column_int64` / `sqlite3_column_text`
- Tusentals enskilda gränsövergångar mellan Go-schemaläggaren och C-stacken

### Begärandepipelining och 64 KiB batchförhämtning

`gosuda.org/sqlite3` eliminerar gränsövergångar per rad genom att konstruera om wire-protokollet kring två mekanismer i `stmt.go`, `rows.go` och `sqlite3/server/sqlite3_hooks.c`:

```text
Go-klient (stmt.go / rows.go)                       C11-arbetare (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Pipelina i 1 flygning:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    loopa sqlite3_step() upp till 128 rader
                                          <=======  Batch 1 (rader 1..128 packade i 64 KiB-plats)
2. Utlös omedelbart förhämtning:
   FetchRowsAsync(128) (Batch 2)          =======>  loopa sqlite3_step() för rader 129..256
   Medan C stegar Batch 2 skannar Go-anroparen
   rader 1..128 från Batch 1!             <=======  Batch 2 klar i delat minne
```

1. **Enkelflygande frågepipelining (`stmt.go`)**: När `QueryContext` eller `Stmt.QueryContext` kör en skrivskyddad sats skickar `s.query()` `reset`, `bind_values`, `stmt_info` (vid första exekveringen) och `fetch_rows` asynkront i en enda flygning i delat minne. Alla fyra operationerna exekveras direkt efter varandra på C-arbetaren och returneras i en enda tur.
2. **Täta C-batchloopar och asynkron förhämtning (`rows.go`)**: Inuti `fetch_rows` (`sqlite3_hooks.c`) stegar C-arbetaren `sqlite3_step(stmt)` i en nativ C-loop och kodar upp till `FetchSize` rader (standard 128) direkt i svarsplatsen i delat minne på 64 KiB tills batchgränsen nås, platsen är full eller `SQLITE_DONE` inträffar. Så snart `rows.ingest()` avkodar Batch 1 utlöser den `FetchRowsAsync` för Batch 2 innan den första raden returneras till anroparen. Medan din Go-kod skannar Batch 1 stegar C-arbetaren redan Batch 2 på en annan CPU-kärna.

På Apple M4 (`darwin/arm64`) visar en jämförelse mellan `gosuda.org/sqlite3` (`CGO_ENABLED=0`) och `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) på samma SQLite-databas effekten av batchning och pipelining:

| Arbetsbelastning                    | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Relativ hastighet                    |
| :---------------------------------- | -------------------------------------: | -----------------------------------: | :----------------------------------- |
| **Punktselektion (`QueryRow` på PK)** |           `3,52 µs/op` (9 B, 1 allokering) |      `3,56 µs/op` (232 B, 11 allokeringar) | **1,01x (paritet, 11x färre allokeringar)** |
| **Skanning av 100 rader (`rows.Next`)** |   `20,41 µs/op` (30,5 KB, 211 allokeringar) |  `36,34 µs/op` (21,8 KB, 309 allokeringar) | **1,78x snabbare**                   |
| **Skanning av 1 000 rader (`rows.Next`)** | `125,92 µs/op` (296 KB, 2 030 allokeringar) | `349,10 µs/op` (216 KB, 3 009 allokeringar) | **2,77x snabbare**                   |
| **Förberedd insert (autocommit)**   |          `5,33 µs/op` (0 B, 0 allokeringar) |       `5,88 µs/op` (120 B, 5 allokeringar) | **1,10x snabbare (0 allokeringar)**  |
| **Transaktionsbatch med 100 rader** |        `189,99 µs/op` (16 B, 1 allokering) | `191,50 µs/op` (12,1 KB, 503 allokeringar) | **1,01x (500x färre allokeringar)**  |

## Avbrytning, sökvägssandlådning och återhämtning av anslutningspoolen

Att köra SQLite i en isolerad arbetare stärker också hur drivrutinen hanterar timeouts, ej betrodd SQL och filsystemsgränser:

- **Deterministisk avbrytning av kontext (`conn.go` och `errors.go`)**: Varje drivrutinsanrop aktiverar en lättviktig `context.AfterFunc`-vakt (`cn.guard`). Om anroparens `context.Context` avbryts eller dess deadline löper ut medan en långsam rekursiv fråga eller låsväntan körs på servern, markerar vakten anslutningen som defekt, dödar den lokala sessionslänken och skickar en out-of-band-ramen `Cancel()`. C-arbetaren anropar omedelbart `sqlite3_interrupt(db)`, vilket frigör `sqlite3_step`, medan Go-drivrutinen mappar den resulterande `SQLITE_INTERRUPT`-statusen till `context.Canceled` eller `context.DeadlineExceeded` och instruerar `database/sql` att ersätta anslutningen i poolen.
- **Katalogsandlådning (`sqlsrv_pathrules.c`)**: C-arbetaren tillämpar `--allow-dir`-sökvägsregler vid varje `open_v2`-anrop, löser upp symboliska länkar och avvisar relativ `..`-traversering utanför den tillåtna katalogen. `sqlite3_load_extension` och `SQLITE_LIMIT_ATTACHED` är inaktiverade som standard så att SQL-injektion inte kan lämna databaskatalogen eller läsa in godtyckliga delade objekt.

## Repositoriets struktur

Repositoriet är strukturerat så att paketet på toppnivå tillhandahåller den drop-in-kompatibla `database/sql`-drivrutinen medan `launcher/` och `sqlite3/` förblir tillgängliga för avancerade anpassade serverdistributioner:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registrering av database/sql-drivrutin ("sqlite3") och Connector-API
├── dsn.go                     # Ren-rums-DSN-parser för mattn/go-sqlite3 och pragma-konfiguration
├── conn.go / tx.go            # Sessionslivscykel, kontextvakt och tillståndsmaskin för transaktioner
├── stmt.go / rows.go          # Enkelflygande begärandepipelining och 64 KiB batchförhämtning
├── errors.go                  # Typade SQLite-primära och utökade felkoder samt matchning av sentinelvärden
├── autolaunch.go / hashes.go  # Konfigurationsfri materialisering av inbäddad server och SHA-256-fastlåsning
├── internal/embedded/         # Plattformsmärkta //go:embed-binärer (linux, darwin, windows)
├── launcher/                  # Konfigurerbar processövervakare för anpassade serverdistributioner
└── sqlite3/                   # Genererad hqq-bindgen Go-klient och C11-serverkälla för SQLite
    └── server/                # C11-serverimplementation (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Komma igång

Installera modulen med `go get` och öppna din databas med `sql.Open("sqlite3", dsn)`:

```bash
go get gosuda.org/sqlite3@v0.3.3540100
```

- **GitHub-repositorium**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Referens för DSN och pragma**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Arkitektur och wire-protokoll**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
