---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql-stuurprogramma via gedeeld-geheugen-IPC'
description: Drop-in database/sql-stuurprogramma voor SQLite 3.54.1 in Go met CGO_ENABLED=0, HQQ-geheugenisolatie tussen workers via gedeeld geheugen, batchgewijze vooraflezing van rijen van 64 KiB en SLSA-geattesteerde binaire bestanden.
language: nl
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` is een `CGO_ENABLED=0` `database/sql`-driver voor Go die de upstream C SQLite `3.54.1` amalgamatie uitvoert binnen een geïsoleerd workerproces via [HQQ](https://gosuda.org/hqq) shared-memory rings. Het registreert zich onder de standaard driver-naam `"sqlite3"`, ondersteunt DSN-queryparameters van `github.com/mattn/go-sqlite3`, bevat SLSA-geattesteerde vooraf gecompileerde serverbinaries voor zes besturingssysteem- en architectuurdoelen, en gebruikt request pipelining met 64 KiB batch prefetching om in-process `cgo` te overtreffen bij query's die meerdere rijen opleveren.

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

## Waarom nog een Go SQLite-driver?

SQLite is een van de meest gebruikte softwarecomponenten ter wereld, maar het gebruik ervan vanuit Go heeft ingenieurs historisch gedwongen tot een ongemakkelijke keuze tussen eenvoud van de build en fidelity van de engine.

Aan de ene kant staat `github.com/mattn/go-sqlite3`, de langdurige standaard binnen de gemeenschap. Deze bindt de officiële C SQLite-amalgamatie rechtstreeks via `cgo`. Men krijgt de echte C-queryplanner, volledige WAL-concurrencysemantiek en voorspelbaar C-geheugenbeheer. De prijs is `CGO_ENABLED=1`: lokale builds en CI-pijplijnen worden trager, minimale scratch- of distroless-containerimages vereisen overeenkomende C-runtimebibliotheken, cross-compilen naar een ander besturingssysteem of CPU-architectuur vereist een volledige C-cross-toolchain, en elke geheugencorruptie in C deelt de adresruimte van het Go-proces.

Aan de andere kant staan pure-Go SQLite-ports die zijn gegenereerd door de C-amalgamatie mechanisch te vertalen naar Go-broncode over een geëmuleerde C-runtime (`libc`). Deze herstellen `CGO_ENABLED=0`, wat builds triviaal maakt. Onder aanhoudende werkbelastingen verhogen de vertaalde C-heap en de allocaties per rij in Go echter de druk op de garbage collector, en subtiele randgevallen in locking, busy timeouts of statement-interruptie kunnen afwijken van native C-builds.

Wij hebben `gosuda.org/sqlite3` gebouwd zodat Go-applicaties niet hoeven te kiezen tussen `CGO_ENABLED=0` en de echte C SQLite-engine. Door de officiële SQLite `3.54.1` C-amalgamatie te compileren in een toegewijde shared-memory-server (`hqq-sqlite-server`) en te communiceren via `gosuda.org/hqq`, wordt uw Go-binary gecompileerd met `CGO_ENABLED=0` terwijl ongewijzigde, `-O3`-geoptimaliseerde C SQLite wordt uitgevoerd over een procesgrens heen.

## Configuratievrije uitvoering: van `go get` tot `sql.Open`

Het vervangen van `github.com/mattn/go-sqlite3` in een bestaande Go-codebase vereist alleen het wijzigen van het blank import-pad. De driver registreert zich als `"sqlite3"` (en `"hqq-sqlite"` voor expliciete selectie) en accepteert dezelfde DSN-querystrings:

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

### Ingebedde SLSA-geattesteerde workerbinaries

Een natuurlijke vraag wanneer men hoort dat een bibliotheek een C-workerproces gebruikt, is: _"Moet ik een afzonderlijke C-binary installeren of meeleveren naast mijn Go-programma?"_

Nee. `gosuda.org/sqlite3` bevat vooraf gecompileerde `hqq-sqlite-server`-binaries voor alle zes belangrijke doelen, rechtstreeks ingebed in platformgetagde Go-bestanden (`internal/embedded/embed_<goos>_<goarch>.go`). Omdat de build-constraints van Go alleen het bestand compileren dat overeenkomt met uw doel-`GOOS` en `GOARCH`, bevat `GOOS=linux GOARCH=arm64 go build` automatisch alleen de ongeveer 1,4 MiB grote `linux/arm64`-serverbinary.

| Doel-OS     | Doel-architectuur (`GOARCH`) | Ingebedde binary                      | Buildtoolchain en herkomst                        |
| :---------- | :--------------------------- | :------------------------------------ | :------------------------------------------------ |
| **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 |

Wanneer `sql.Open` de eerste verbinding tot stand brengt, materialiseert `autolaunch.go` de ingebedde binary in de gebruikerscachemap (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifieert het cryptografische SHA-256-digest ervan tegen de vastgepinde `ReleaseHashes`-map in `hashes.go` via `gosuda.org/hqq/secureexec`, en start de worker met `--parent-stdin`, zodat de levensduur ervan strikt is gekoppeld aan uw Go-proces.

### Clean-room `mattn/go-sqlite3` DSN-compatibiliteit

Om als werkelijk drop-in-vervanging te fungeren, implementeert de DSN-parser (`dsn.go`) een clean-room replica van de verbindings- en pragma-instellingen die in `mattn/go-sqlite3`-codebases worden gebruikt:

| DSN-queryparameter                                                                            | Geaccepteerde waarden                                   | Effect in SQLite                                                                                     |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Geheel getal in milliseconden (bijv. `5000`)            | Roept `sqlite3_busy_timeout(db, ms)` aan bij het openen van de sessie.                               |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Voert `PRAGMA journal_mode = <mode>` uit.                                                            |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Selecteert `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` of `BEGIN DEFERRED` voor `db.BeginTx`.               |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Voert `PRAGMA foreign_keys = ON/OFF` uit.                                                            |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Voert `PRAGMA synchronous = <level>` uit.                                                            |
| `_cache_size`                                                                                 | Geheel aantal pagina's of `-KiB` (bijv. `-20000`)       | Voert `PRAGMA cache_size = <val>` uit.                                                               |
| `_loc`                                                                                        | `auto` of IANA-tijdzone (bijv. `UTC`, `Asia/Seoul`)     | Parseert `DATETIME`/`TIMESTAMP`-tekstkolommen naar `*time.Time` in de doellocatie.                   |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Stelt `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` of `SQLITE_OPEN_MEMORY` in. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Boolean of modusgeheeltallen                            | Past de bijbehorende sessie-`PRAGMA` toe bij initialisatie van de verbinding.                        |

## Procesisolatie omzetten in een doorvoervoordeel

Op het eerste gezicht lijkt het uitvoeren van SQLite in een afzonderlijk proces noodzakelijkerwijs trager dan het aanroepen van `sqlite3_step` in-process via `cgo`. In de praktijk gebeurt het tegenovergestelde zodra een query meer dan één rij teruggeeft.

### De verborgen kosten van rij-voor-rij `cgo`-aanroepen

In een conventionele `cgo`-SQLite-driver doorloopt `database/sql` de resultaten één rij en één kolom tegelijk. Het lezen van 1.000 rijen met 4 kolommen veroorzaakt:

- 1.000 `cgo`-aanroepen naar `sqlite3_step`
- 4.000 `cgo`-aanroepen naar `sqlite3_column_type` en `sqlite3_column_int64` / `sqlite3_column_text`
- Duizenden afzonderlijke grensovergangen tussen de Go-scheduler en de C-stack

### Request pipelining en 64 KiB batch prefetching

`gosuda.org/sqlite3` elimineert grensovergangen per rij door het wire-protocol te herontwerpen rond twee mechanismen in `stmt.go`, `rows.go` en `sqlite3/server/sqlite3_hooks.c`:

```text
Go Client (stmt.go / rows.go)                       C11 Worker (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Pipeline in 1 flight:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    loop sqlite3_step() up to 128 rows
                                          <=======  Batch 1 (rows 1..128 packed in 64 KiB slot)
2. Immediately fire prefetch:
   FetchRowsAsync(128) (Batch 2)          =======>  loop sqlite3_step() for rows 129..256
   While C steps Batch 2, Go caller
   scans rows 1..128 from Batch 1!        <=======  Batch 2 ready in shared memory
```

1. **Single-Flight Query Pipelining (`stmt.go`)**: Wanneer `QueryContext` of `Stmt.QueryContext` een alleen-lezen statement uitvoert, dient `s.query()` `reset`, `bind_values`, `stmt_info` (bij eerste uitvoering) en `fetch_rows` asynchroon in één enkele shared-memory flight in. Alle vier bewerkingen worden achter elkaar op de C-worker uitgevoerd en keren terug in één round trip.
2. **Strakke C-batchlussen en asynchrone prefetch (`rows.go`)**: Binnen `fetch_rows` (`sqlite3_hooks.c`) voert de C-worker `sqlite3_step(stmt)` uit in een native C-lus en codeert hij tot `FetchSize` rijen (standaard 128) rechtstreeks in de 64 KiB shared-memory responseslot, totdat de batchlimiet is bereikt, het slot vol is of `SQLITE_DONE` optreedt. Zodra `rows.ingest()` Batch 1 decodeert, vuurt het `FetchRowsAsync` voor Batch 2 af voordat de eerste rij aan de aanroeper wordt teruggegeven. Terwijl uw Go-code Batch 1 scant, staat de C-worker al op een andere CPU-kern Batch 2 te stappen.

Op Apple M4 (`darwin/arm64`) toont een vergelijking van `gosuda.org/sqlite3` (`CGO_ENABLED=0`) met `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) op dezelfde SQLite-database het effect van batching en pipelining:

| Werkbelasting                       | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) |  `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Relatieve snelheid                   |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Point Select (`QueryRow` op PK)** |            `3,52 µs/op` (9 B, 1 alloc) |       `3,56 µs/op` (232 B, 11 allocs) | **1,01x (gelijk, 11x minder allocs)** |
| **100-rijen scan (`rows.Next`)**    |    `20,41 µs/op` (30,5 KB, 211 allocs) |   `36,34 µs/op` (21,8 KB, 309 allocs) | **1,78x sneller**                    |
| **1.000-rijen scan (`rows.Next`)**  |  `125,92 µs/op` (296 KB, 2.030 allocs) | `349,10 µs/op` (216 KB, 3.009 allocs) | **2,77x sneller**                    |
| **Prepared Insert (Autocommit)**    |           `5,33 µs/op` (0 B, 0 allocs) |        `5,88 µs/op` (120 B, 5 allocs) | **1,10x sneller (0 allocs)**         |
| **100-rijen transactiebatch**       |         `189,99 µs/op` (16 B, 1 alloc) |  `191,50 µs/op` (12,1 KB, 503 allocs) | **1,01x (500x minder allocs)**       |

## Annulering, padsandboxing en herstel van de connection pool

Het uitvoeren van SQLite in een geïsoleerde worker versterkt ook de manier waarop de driver omgaat met time-outs, onbetrouwbare SQL en bestandssysteemgrenzen:

- **Deterministische contextannulering (`conn.go` en `errors.go`)**: Elke driveraanroep activeert een lichtgewicht `context.AfterFunc`-guard (`cn.guard`). Als de `context.Context` van de aanroeper wordt geannuleerd of de deadline verstrijkt terwijl een trage recursieve query of lock-wachttijd op de server draait, markeert de guard de verbinding als defect, verbreekt de lokale sessieverbinding en stuurt een out-of-band `Cancel()`-frame. De C-worker roept onmiddellijk `sqlite3_interrupt(db)` aan, waardoor `sqlite3_step` wordt ontblokkeerd, terwijl de Go-driver de resulterende `SQLITE_INTERRUPT`-status vertaalt naar `context.Canceled` of `context.DeadlineExceeded` en `database/sql` opdraagt de verbinding in de pool te vervangen.
- **Directorysandboxing (`sqlsrv_pathrules.c`)**: De C-worker handhaaft `--allow-dir`-padregels bij elke `open_v2`-aanroep, lost symbolische koppelingen op en weigert relatieve `..`-doorloop buiten de toegestane map. `sqlite3_load_extension` en `SQLITE_LIMIT_ATTACHED` zijn standaard uitgeschakeld, zodat SQL-injectie niet uit de databasemap kan ontsnappen of willekeurige gedeelde objecten kan laden.

## Repositorystructuur

De repository is zo gestructureerd dat het pakket op het hoogste niveau de drop-in `database/sql`-driver biedt, terwijl `launcher/` en `sqlite3/` toegankelijk blijven voor geavanceerde implementaties met aangepaste servers:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registratie van de database/sql-driver ("sqlite3") en Connector-API
├── dsn.go                     # Clean-room mattn/go-sqlite3 DSN-parser en pragma-configuratie
├── conn.go / tx.go            # Sessielevenscyclus, contextguard en transactiestatusmachine
├── stmt.go / rows.go          # Single-flight request pipelining en 64 KiB batch prefetching
├── errors.go                  # Getypeerde SQLite primaire/uitgebreide foutcodes en sentinelmatching
├── autolaunch.go / hashes.go  # Configuratievrije materialisatie van de ingebedde server en SHA-256-pinning
├── internal/embedded/         # Platformgetagde //go:embed-binaries (linux, darwin, windows)
├── launcher/                  # Configureerbare processupervisor voor aangepaste serverimplementaties
└── sqlite3/                   # Gegenereerde hqq-bindgen Go-client en C11 SQLite-serverbroncode
    └── server/                # C11-serverimplementatie (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Aan de slag

Installeer de module met `go get` en open uw database met `sql.Open("sqlite3", dsn)`:

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

- **GitHub-repository**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **DSN- en pragmareferentie**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Architectuur en wire-protocol**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
