---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql Driver over Shared-Memory IPC'
description: Drop-in database/sql SQLite 3.54.1-driver for Go med CGO_ENABLED=0, HQQ-delt minneisolasjon for arbeidsprosesser, forhåndshenting av batchrader på 64 KiB og SLSA-attesterte binærfiler.
language: "no"
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` er en `database/sql`-driver for Go med `CGO_ENABLED=0` som kjører den offisielle C-SQLite `3.54.1`-amalgamasjonen i en isolert arbeidsprosess via [HQQ](https://gosuda.org/hqq)-delt minneringbuffere. Den registreres under det standardiserte driver-navnet `"sqlite3"`, støtter DSN-spørringsparametere fra `github.com/mattn/go-sqlite3`, inkluderer SLSA-attesterte, forhåndskompilerte serverbinærfiler for seks målplattformer for operativsystem og arkitektur, og bruker forespørselspipelining med forhåndshenting av batcher på 64 KiB for å yte bedre enn in-process `cgo` ved spørringer som returnerer flere rader.

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

## Hvorfor en ny Go-driver for SQLite?

SQLite er en av de mest utbredte programvarekomponentene i verden, men bruk av den fra Go har tradisjonelt tvunget utviklere til et ubehagelig valg mellom byggeenkelhet og trofasthet mot selve databasemotoren.

På den ene siden står `github.com/mattn/go-sqlite3`, den langvarige standarden i fellesskapet. Den omslutter den offisielle C-SQLite-amalgamasjonen direkte via `cgo`. Man får den reelle C-spørringsplanleggeren, full WAL-samtidighetssemantikk og forutsigbar minnehåndtering i C. Prisen er `CGO_ENABLED=1`: lokale bygg og CI-pipelines blir tregere, minimale scratch- eller distroless-containerbilder krever tilsvarende C-kjøretidsbiblioteker, krysskompilering til et annet operativsystem eller en annen CPU-arkitektur krever en fullstendig C-kryssverktøykjede, og eventuell minnekorrupsjon i C deler adresserommet med Go-prosessen.

På den andre siden finnes rene Go-portinger av SQLite som er generert ved mekanisk oversettelse av C-amalgamasjonen til Go-kildekode over et emulert C-kjøretidsmiljø (`libc`). De gjenoppretter `CGO_ENABLED=0`, noe som gjør byggene trivielle. Under vedvarende arbeidslast øker imidlertid den oversatte C-heapen og de per-rad-allokeringene i Go belastningen på søppelinnsamlingen, og subtile tilfeller knyttet til låsing, busy-timeouts eller avbrytelse av setninger kan avvike fra native C-bygg.

Vi bygde `gosuda.org/sqlite3` for at Go-applikasjoner ikke skal måtte velge mellom `CGO_ENABLED=0` og den reelle C-SQLite-motoren. Ved å kompilere den offisielle SQLite `3.54.1` C-amalgamasjonen til en dedikert server med delt minne (`hqq-sqlite-server`) og kommunisere over `gosuda.org/hqq`, kompileres din Go-binærfil med `CGO_ENABLED=0`, samtidig som den kjører uendret, `-O3`-optimalisert C-SQLite på tvers av en prosessgrense.

## Konfigurasjonsfri kjøring: Fra `go get` til `sql.Open`

Å erstatte `github.com/mattn/go-sqlite3` i en eksisterende Go-kodebase krever kun endring av den blanke importstien. Driveren registreres som `"sqlite3"` (og `"hqq-sqlite"` for eksplisitt valg) og godtar de samme DSN-spørrestrengene:

```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)
}
```

### Innebygde SLSA-attesterte arbeidsbinærfiler

Et naturlig spørsmål når man hører at et bibliotek bruker en C-arbeidsprosess, er: _"Må jeg installere eller distribuere en separat C-binærfil sammen med Go-programmet mitt?"_

Nei. `gosuda.org/sqlite3` inkluderer forhåndskompilerte `hqq-sqlite-server`-binærfiler for alle seks store målplattformer direkte i plattformmerkede Go-filer (`internal/embedded/embed_<goos>_<goarch>.go`). Fordi Gos byggebegrensninger kun kompilerer filen som samsvarer med målet ditt `GOOS` og `GOARCH`, vil krysskompilering med `GOOS=linux GOARCH=arm64 go build` automatisk kun inkludere den ca. 1,4 MiB store `linux/arm64`-serverbinærfilen.

| Målsystem   | Målarkitektur (`GOARCH`) | Innebygd binærfil                     | Byggeverktøy og 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` etablerer sin første forbindelse, materialiserer `autolaunch.go` den innebygde binærfilen i brukerens cachekatalog (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifiserer dens kryptografiske SHA-256-sammendrag mot det fastlåste `ReleaseHashes`-kartet i `hashes.go` via `gosuda.org/hqq/secureexec`, og starter arbeidsprosessen med `--parent-stdin` slik at levetiden er strengt knyttet til Go-prosessen din.

### Ren-rom-kompatibilitet med DSN for `mattn/go-sqlite3`

For å fungere som en reell drop-in-erstatning implementerer DSN-parseren (`dsn.go`) en ren-rom-replika av tilkoblings- og pragma-innstillingene som brukes på tvers av `mattn/go-sqlite3`-kodebaser:

| DSN-spørringsparameter                                                                        | Godtatte verdier                                        | SQLite-effekt                                                                                        |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Heltall i millisekunder (f.eks. `5000`)                 | Kaller `sqlite3_busy_timeout(db, ms)` ved åpning av økt.                                             |
| `_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`                                                                                 | Heltall i sider 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ålplasseringen.                      |
| `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` | Boolske verdier eller modusheltall                      | Anvender tilsvarende økt-`PRAGMA` ved initialisering av forbindelsen.                                |

## Å gjøre prosessisolasjon til en gjennomstrømningsfordel

Ved første øyekast kan det virke som om kjøring av SQLite i en egen prosess må være tregere enn å kalle `sqlite3_step` in-process via `cgo`. I praksis skjer det motsatte så snart en spørring returnerer mer enn én rad.

### De skjulte kostnadene ved rad-for-rad-kall i `cgo`

I en konvensjonell `cgo`-driver for SQLite driver `database/sql` iterasjonen én rad og én kolonne om gangen. Lesing av 1 000 rader med 4 kolonner utløser:

- 1 000 `cgo`-kall til `sqlite3_step`
- 4 000 `cgo`-kall til `sqlite3_column_type` og `sqlite3_column_int64` / `sqlite3_column_text`
- Tusenvis av enkeltstående grensekryssinger mellom Go-skjedulereren og C-stakken

### Forespørselspipelining og forhåndshenting av batcher på 64 KiB

`gosuda.org/sqlite3` eliminerer grensekryssinger per rad ved å redesigne wire-protokollen rundt to mekanismer i `stmt.go`, `rows.go` og `sqlite3/server/sqlite3_hooks.c`:

```text
Go-klient (stmt.go / rows.go)                       C11-arbeider (sqlite3_hooks.c)
-----------------------------                       ------------------------------
1. Pipeline i 1 flyt:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    løkke over sqlite3_step() opptil 128 rader
                                          <=======  Batch 1 (rad 1..128 pakket i 64 KiB-slot)
2. Utløs forhåndshenting umiddelbart:
   FetchRowsAsync(128) (Batch 2)          =======>  løkke over sqlite3_step() for rad 129..256
   Mens C steger Batch 2, skanner Go-kalleren
   rad 1..128 fra Batch 1!                <=======  Batch 2 klar i delt minne
```

1. **Pipelining av spørringer i én flyt (`stmt.go`)**: Når `QueryContext` eller `Stmt.QueryContext` kjører en skrivebeskyttet setning, sender `s.query()` `reset`, `bind_values`, `stmt_info` (ved første kjøring) og `fetch_rows` asynkront i én enkelt flyt i delt minne. Alle fire operasjonene kjøres etter hverandre på C-arbeideren og returneres i én rundereise.
2. **Stramme C-batchløkker og asynkron forhåndshenting (`rows.go`)**: Inne i `fetch_rows` (`sqlite3_hooks.c`) kjører C-arbeideren `sqlite3_step(stmt)` i en nativ C-løkke og koder opptil `FetchSize` rader (standard 128) direkte inn i responsslotet i delt minne på 64 KiB, inntil batchgrensen er nådd, slotet er fullt eller `SQLITE_DONE` inntreffer. Så snart `rows.ingest()` har dekodet Batch 1, utløser den `FetchRowsAsync` for Batch 2 før den returnerer første rad til kalleren. Mens Go-koden din skanner Batch 1, steger C-arbeideren allerede Batch 2 på en annen CPU-kjerne.

På Apple M4 (`darwin/arm64`) viser sammenligningen mellom `gosuda.org/sqlite3` (`CGO_ENABLED=0`) og `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) på samme SQLite-database effekten av batching og pipelining:

| Arbeidslast                         | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`)  | Relativ hastighet                    |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Punktvalg (`QueryRow` på PK)**    |            `3.52 µs/op` (9 B, 1 alloc) |       `3.56 µs/op` (232 B, 11 allok.) | **1,01x (paritet, 11x færre allok.)** |
| **Skanning av 100 rader (`rows.Next`)** |    `20.41 µs/op` (30.5 KB, 211 allok.) |   `36.34 µs/op` (21.8 KB, 309 allok.) | **1,78x raskere**                    |
| **Skanning av 1 000 rader (`rows.Next`)** |  `125.92 µs/op` (296 KB, 2 030 allok.) | `349.10 µs/op` (216 KB, 3 009 allok.) | **2,77x raskere**                    |
| **Forberedt innsetting (autocommit)** |           `5.33 µs/op` (0 B, 0 allok.) |        `5.88 µs/op` (120 B, 5 allok.) | **1,10x raskere (0 allok.)**         |
| **Transaksjonsbatch med 100 rader** |         `189.99 µs/op` (16 B, 1 alloc) |  `191.50 µs/op` (12.1 KB, 503 allok.) | **1,01x (500x færre allok.)**        |

## Kansellering, stisandboxing og gjenoppretting av tilkoblingspool

Å kjøre SQLite i en isolert arbeider styrker også måten driveren håndterer tidsavbrudd, upålitelig SQL og filsystemgrenser på:

- **Deterministisk kansellering av kontekst (`conn.go` og `errors.go`)**: Hvert driverkall aktiverer en lettvekts `context.AfterFunc`-vakt (`cn.guard`). Dersom kallerens `context.Context` kanselleres, eller fristen utløper, mens en treg rekursiv spørring eller låsevent kjører på serveren, markerer vakten tilkoblingen som ugyldig, dreper den lokale sesjonsforbindelsen og sender en `Cancel()`-ramme utenfor båndet. C-arbeideren kaller `sqlite3_interrupt(db)` umiddelbart, slik at `sqlite3_step` frigjøres, mens Go-driveren oversetter den resulterende `SQLITE_INTERRUPT`-statusen til `context.Canceled` eller `context.DeadlineExceeded` og instruerer `database/sql` om å erstatte tilkoblingen i poolen.
- **Katalogsandboxing (`sqlsrv_pathrules.c`)**: C-arbeideren håndhever `--allow-dir`-stiregler ved hvert `open_v2`-kall, løser symbolske lenker og avviser relativ `..`-traversering utenfor det tillatte katalogen. `sqlite3_load_extension` og `SQLITE_LIMIT_ATTACHED` er deaktivert som standard, slik at SQL-injeksjon ikke kan unnslippe databasekatalogen eller laste inn vilkårlige delte objekter.

## Repositoriets struktur

Repositoriet er strukturert slik at pakken på toppnivå leverer den drop-in `database/sql`-driveren, mens `launcher/` og `sqlite3/` forblir tilgjengelige for avanserte distribusjoner med egendefinert server:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registrering av database/sql-driver ("sqlite3") og Connector-API
├── dsn.go                     # Ren-rom DSN-parser for mattn/go-sqlite3 og pragma-konfigurasjon
├── conn.go / tx.go            # Livssyklus for økt, kontekstvakt og tilstandsmaskin for transaksjoner
├── stmt.go / rows.go          # Pipelining av forespørsler i én flyt og forhåndshenting av batcher på 64 KiB
├── errors.go                  # Typede primære og utvidede SQLite-feilkoder og matching av sentinelverdier
├── autolaunch.go / hashes.go  # Konfigurasjonsfri materialisering av innebygd server og SHA-256-fastlåsing
├── internal/embedded/         # Plattformmerkede //go:embed-binærfiler (linux, darwin, windows)
├── launcher/                  # Konfigurerbar prosessovervåker for egendefinerte serverdistribusjoner
└── sqlite3/                   # Generert hqq-bindgen Go-klient og C11-kildekode for SQLite-server
    └── server/                # C11-serverimplementasjon (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Kom i gang

Installer modulen med `go get` og åpne databasen din 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)
- **DSN- og pragma-referanse**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Arkitektur og wire-protokoll**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
