---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql-Treiber über Shared-Memory-IPC'
description: Drop-in-kompatibler database/sql-SQLite-3.54.1-Treiber für Go mit CGO_ENABLED=0, HQQ-Worker-Isolation über gemeinsamen Speicher, Batch-Zeilen-Prefetching mit 64 KiB sowie SLSA-attestierten Binärdateien.
language: de
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` ist ein `database/sql`-Treiber für Go mit `CGO_ENABLED=0`, der den upstream C-SQLite-`3.54.1`-Amalgamation innerhalb eines isolierten Worker-Prozesses über [HQQ](https://gosuda.org/hqq)-Shared-Memory-Ringe ausführt. Er registriert sich unter dem standardmäßigen Treibernamen `"sqlite3"`, unterstützt die DSN-Query-Parameter von `github.com/mattn/go-sqlite3`, bettet SLSA-attestierte, vorkompilierte Server-Binaries für sechs Betriebssystem- und Architektur-Zielplattformen ein und nutzt Request-Pipelining sowie 64-KiB-Batch-Prefetching, um In-Process-`cgo` bei mehrzeiligen Abfragen zu übertreffen.

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

## Warum ein weiterer Go-SQLite-Treiber?

SQLite gehört zu den am weitesten verbreiteten Softwarekomponenten der Welt, dennoch zwang die Nutzung aus Go Entwickler bisher zu einer unbequemen Wahl zwischen Build-Einfachheit und Engine-Treue.

Auf der einen Seite steht `github.com/mattn/go-sqlite3`, der langjährige Standard der Community. Er bindet das offizielle C-SQLite-Amalgamation direkt über `cgo` ein. Man erhält den echten C-Query-Planer, die vollständige WAL-Nebenläufigkeitssemantik und ein vorhersagbares C-Speichermanagement. Der Preis ist `CGO_ENABLED=1`: Lokale Builds und CI-Pipelines werden langsamer, minimale Scratch- oder Distroless-Container-Images benötigen passende C-Laufzeitbibliotheken, die Cross-Kompilierung für ein anderes Betriebssystem oder eine andere CPU-Architektur erfordert eine vollständige C-Cross-Toolchain, und jede Speicherkorruption im C-Code teilt sich den Adressraum des Go-Prozesses.

Auf der anderen Seite stehen reine Go-Portierungen von SQLite, die durch mechanische Übersetzung des C-Amalgamation in Go-Quellcode über eine emulierte C-Laufzeitumgebung (`libc`) entstanden sind. Sie stellen `CGO_ENABLED=0` wieder her, wodurch Builds trivial werden. Unter dauerhafter Last erhöhen der übersetzte C-Heap und die Go-Allokationen pro Zeile jedoch den Druck auf die Garbage Collection, und subtile Randfälle bei Sperren, Busy-Timeouts oder Statement-Unterbrechung können von nativen C-Builds abweichen.

Wir haben `gosuda.org/sqlite3` entwickelt, damit Go-Anwendungen nicht zwischen `CGO_ENABLED=0` und der echten C-SQLite-Engine wählen müssen. Indem das offizielle C-Amalgamation von SQLite `3.54.1` in einen dedizierten Shared-Memory-Server (`hqq-sqlite-server`) kompiliert wird und die Kommunikation über `gosuda.org/hqq` erfolgt, wird Ihr Go-Binary mit `CGO_ENABLED=0` gebaut, während unveränderte, mit `-O3` optimierte C-SQLite-Engine über eine Prozessgrenze hinweg ausgeführt wird.

## Zero-Config-Ausführung: Von `go get` bis `sql.Open`

Der Ersatz von `github.com/mattn/go-sqlite3` in einer bestehenden Go-Codebasis erfordert nur die Änderung des Blank-Import-Pfads. Der Treiber registriert sich als `"sqlite3"` (sowie `"hqq-sqlite"` zur expliziten Auswahl) und akzeptiert dieselben DSN-Query-Strings:

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

### Eingebettete SLSA-attestierte Worker-Binaries

Eine naheliegende Frage, wenn man hört, dass eine Bibliothek einen C-Worker-Prozess verwendet, lautet: _„Muss ich ein separates C-Binary installieren oder zusammen mit meinem Go-Programm ausliefern?“_

Nein. `gosuda.org/sqlite3` bettet vorkompilierte `hqq-sqlite-server`-Binaries für alle sechs wichtigen Zielplattformen direkt in plattformgekennzeichnete Go-Dateien ein (`internal/embedded/embed_<goos>_<goarch>.go`). Da die Build-Constraints von Go nur die Datei kompilieren, die zu Ihrem Ziel-`GOOS` und `GOARCH` passt, bettet eine Cross-Kompilierung mit `GOOS=linux GOARCH=arm64 go build` automatisch nur das etwa 1,4 MiB große Server-Binary für `linux/arm64` ein.

| Ziel-Betriebssystem | Ziel-Architektur (`GOARCH`) | Eingebettetes Binary                  | Build-Toolchain und Herkunftsnachweis             |
| :------------------ | :-------------------------- | :------------------------------------ | :------------------------------------------------ |
| **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 |

Wenn `sql.Open` die erste Verbindung herstellt, materialisiert `autolaunch.go` das eingebettete Binary im Benutzer-Cache-Verzeichnis (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifiziert dessen kryptographischen SHA-256-Digest gegen die fixierte `ReleaseHashes`-Map in `hashes.go` über `gosuda.org/hqq/secureexec` und startet den Worker mit `--parent-stdin`, sodass seine Lebensdauer strikt an Ihren Go-Prozess gebunden ist.

### Clean-Room-DSN-Kompatibilität zu `mattn/go-sqlite3`

Um als echter Drop-in-Ersatz zu funktionieren, implementiert der DSN-Parser (`dsn.go`) eine Clean-Room-Nachbildung der Verbindungs- und Pragma-Schalter, die in `mattn/go-sqlite3`-Codebasen verwendet werden:

| DSN-Query-Parameter                                                                           | Akzeptierte Werte                                       | SQLite-Effekt                                                                                        |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Ganzzahlige Millisekunden (z. B. `5000`)                | Ruft `sqlite3_busy_timeout(db, ms)` beim Öffnen der Sitzung auf.                                     |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Führt `PRAGMA journal_mode = <mode>` aus.                                                            |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Wählt `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` oder `BEGIN DEFERRED` für `db.BeginTx`.                   |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Führt `PRAGMA foreign_keys = ON/OFF` aus.                                                            |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Führt `PRAGMA synchronous = <level>` aus.                                                            |
| `_cache_size`                                                                                 | Ganzzahlige Seiten oder `-KiB` (z. B. `-20000`)         | Führt `PRAGMA cache_size = <val>` aus.                                                               |
| `_loc`                                                                                        | `auto` oder IANA-Zeitzone (z. B. `UTC`, `Asia/Seoul`)   | Parst `DATETIME`/`TIMESTAMP`-Textspalten in `*time.Time` in der Zielzeitzone.                        |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Setzt `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` oder `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Boolesche Werte oder Modus-Ganzzahlen                    | Wendet das entsprechende Sitzungs-`PRAGMA` bei der Verbindungsinitialisierung an.                    |

## Prozessisolation als Durchsatzvorteil

Auf den ersten Blick scheint das Ausführen von SQLite in einem separaten Prozess langsamer sein zu müssen als der In-Process-Aufruf von `sqlite3_step` über `cgo`. In der Praxis geschieht das Gegenteil, sobald eine Abfrage mehr als eine einzige Zeile zurückgibt.

### Die versteckten Kosten zeilenweiser `cgo`-Aufrufe

In einem herkömmlichen `cgo`-SQLite-Treiber iteriert `database/sql` jeweils eine Zeile und eine Spalte nach der anderen. Das Lesen von 1.000 Zeilen mit 4 Spalten löst aus:

- 1.000 `cgo`-Aufrufe von `sqlite3_step`
- 4.000 `cgo`-Aufrufe von `sqlite3_column_type` und `sqlite3_column_int64` / `sqlite3_column_text`
- Tausende einzelne Grenzübergänge zwischen dem Go-Scheduler und dem C-Stack

### Request-Pipelining und 64-KiB-Batch-Prefetching

`gosuda.org/sqlite3` eliminiert zeilenweise Grenzübergänge, indem das Wire-Protokoll um zwei Mechanismen in `stmt.go`, `rows.go` und `sqlite3/server/sqlite3_hooks.c` neu gestaltet wird:

```text
Go-Client (stmt.go / rows.go)                       C11-Worker (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Pipeline in 1 Flug:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    Schleife sqlite3_step() bis zu 128 Zeilen
                                          <=======  Batch 1 (Zeilen 1..128 gepackt in 64-KiB-Slot)
2. Sofort Prefetch auslösen:
   FetchRowsAsync(128) (Batch 2)          =======>  Schleife sqlite3_step() für Zeilen 129..256
   Während C Batch 2 abarbeitet, scannt der
   Go-Aufrufer Zeilen 1..128 aus Batch 1!  <=======  Batch 2 bereit im Shared Memory
```

1. **Single-Flight-Query-Pipelining (`stmt.go`)**: Wenn `QueryContext` oder `Stmt.QueryContext` eine schreibgeschützte Anweisung ausführt, übermittelt `s.query()` `reset`, `bind_values`, `stmt_info` (bei der ersten Ausführung) und `fetch_rows` asynchron in einem einzigen Shared-Memory-Flug. Alle vier Operationen laufen auf dem C-Worker nacheinander ab und kehren in einem einzigen Roundtrip zurück.
2. **Enge C-Batch-Schleifen und asynchrones Prefetching (`rows.go`)**: Innerhalb von `fetch_rows` (`sqlite3_hooks.c`) führt der C-Worker `sqlite3_step(stmt)` in einer nativen C-Schleife aus und kodiert bis zu `FetchSize` Zeilen (Standard 128) direkt in den 64-KiB-Shared-Memory-Antwortslot, bis das Batch-Limit erreicht ist, der Slot voll ist oder `SQLITE_DONE` auftritt. Sobald `rows.ingest()` Batch 1 dekodiert, löst es `FetchRowsAsync` für Batch 2 aus, bevor die erste Zeile an den Aufrufer zurückgegeben wird. Während Ihr Go-Code Batch 1 scannt, führt der C-Worker bereits auf einem anderen CPU-Kern `sqlite3_step` für Batch 2 aus.

Auf Apple M4 (`darwin/arm64`) zeigt der Vergleich von `gosuda.org/sqlite3` (`CGO_ENABLED=0`) mit `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) auf derselben SQLite-Datenbank den Effekt von Batching und Pipelining:

| Workload                            | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Relative Geschwindigkeit             |
| :---------------------------------- | -------------------------------------: | -----------------------------------: | :----------------------------------- |
| **Punktabfrage (`QueryRow` per PK)** |           `3,52 µs/op` (9 B, 1 Alloc.) |      `3,56 µs/op` (232 B, 11 Alloc.) | **1,01x (Parität, 11x weniger Allok.)** |
| **100-Zeilen-Scan (`rows.Next`)**   |   `20,41 µs/op` (30,5 KB, 211 Allok.)  |  `36,34 µs/op` (21,8 KB, 309 Allok.) | **1,78x schneller**                  |
| **1.000-Zeilen-Scan (`rows.Next`)** | `125,92 µs/op` (296 KB, 2.030 Allok.)  | `349,10 µs/op` (216 KB, 3.009 Allok.) | **2,77x schneller**                  |
| **Prepared Insert (Autocommit)**    |          `5,33 µs/op` (0 B, 0 Allok.)  |       `5,88 µs/op` (120 B, 5 Allok.) | **1,10x schneller (0 Allok.)**       |
| **100-Zeilen-Transaktions-Batch**   |        `189,99 µs/op` (16 B, 1 Alloc.) | `191,50 µs/op` (12,1 KB, 503 Allok.) | **1,01x (500x weniger Allok.)**      |

## Abbruch, Pfad-Sandboxing und Wiederherstellung des Connection-Pools

Die Ausführung von SQLite in einem isolierten Worker stärkt außerdem den Umgang des Treibers mit Timeouts, nicht vertrauenswürdigem SQL und Dateisystemgrenzen:

- **Deterministischer Context-Abbruch (`conn.go` und `errors.go`)**: Jeder Treiberaufruf aktiviert eine leichtgewichtige `context.AfterFunc`-Schutzfunktion (`cn.guard`). Wird der `context.Context` des Aufrufers abgebrochen oder läuft seine Deadline ab, während auf dem Server eine langsame rekursive Abfrage oder ein Sperrwartevorgang läuft, markiert der Guard die Verbindung als fehlerhaft, beendet die lokale Sitzungsverbindung und sendet ein Out-of-Band-`Cancel()`-Frame. Der C-Worker ruft sofort `sqlite3_interrupt(db)` auf, wodurch `sqlite3_step` entblockiert wird, während der Go-Treiber den resultierenden `SQLITE_INTERRUPT`-Status auf `context.Canceled` oder `context.DeadlineExceeded` abbildet und `database/sql` anweist, die Verbindung im Pool zu ersetzen.
- **Verzeichnis-Sandboxing (`sqlsrv_pathrules.c`)**: Der C-Worker erzwingt die `--allow-dir`-Pfadregeln bei jedem `open_v2`-Aufruf, löst Symlinks auf und weist relative `..`-Traversierung außerhalb des erlaubten Verzeichnisses zurück. `sqlite3_load_extension` und `SQLITE_LIMIT_ATTACHED` sind standardmäßig deaktiviert, sodass SQL-Injection weder das Datenbankverzeichnis verlassen noch beliebige Shared Objects laden kann.

## Repository-Struktur

Das Repository ist so aufgebaut, dass das Paket auf oberster Ebene den Drop-in-`database/sql`-Treiber bereitstellt, während `launcher/` und `sqlite3/` für fortgeschrittene Deployments mit eigenem Server zugänglich bleiben:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registrierung des database/sql-Treibers ("sqlite3") und Connector-API
├── dsn.go                     # Clean-Room-DSN-Parser für mattn/go-sqlite3 und Pragma-Konfiguration
├── conn.go / tx.go            # Sitzungslebenszyklus, Context-Guard und Zustandsautomat für Transaktionen
├── stmt.go / rows.go          # Single-Flight-Request-Pipelining und 64-KiB-Batch-Prefetching
├── errors.go                  # Typisierte primäre/erweiterte SQLite-Fehlercodes und Sentinel-Abgleich
├── autolaunch.go / hashes.go  # Zero-Config-Materialisierung des eingebetteten Servers und SHA-256-Fixierung
├── internal/embedded/         # Plattformgekennzeichnete //go:embed-Binaries (linux, darwin, windows)
├── launcher/                  # Konfigurierbarer Prozess-Supervisor für eigene Serverdeployments
└── sqlite3/                   # Generierter hqq-bindgen-Go-Client und C11-SQLite-Serverquellcode
    └── server/                # C11-Serverimplementierung (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Erste Schritte

Installieren Sie das Modul mit `go get` und öffnen Sie Ihre Datenbank mit `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- und Pragma-Referenz**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Architektur und Wire-Protokoll**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
