---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql Driver nad Shared-Memory IPC'
description: Ovladač SQLite 3.54.1 pro Go kompatibilní s rozhraním database/sql, bez nutnosti CGO (CGO_ENABLED=0), s izolací workerů přes sdílenou paměť HQQ, předčítáním řádků po dávkách o velikosti 64 KiB a binárními soubory opatřenými attestací SLSA.
language: cs
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` je ovladač `database/sql` pro Go s `CGO_ENABLED=0`, který spouští upstream C SQLite `3.54.1` amalgamaci uvnitř izolovaného pracovního procesu přes sdílené paměťové kruhové fronty [HQQ](https://gosuda.org/hqq). Registruje se pod standardním názvem ovladače `"sqlite3"`, podporuje parametry DSN dotazu z `github.com/mattn/go-sqlite3`, obsahuje vložené předkompilované serverové binárky s SLSA atestací pro šest cílových operačních systémů a architektur a využívá pipelining požadavků s předběžným načítáním dávek o velikosti 64 KiB, díky čemuž překonává in-process `cgo` u dotazů vracejících více řádků.

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

## Proč další ovladač SQLite pro Go?

SQLite patří mezi nejrozšířenější software na světě, přesto se její používání v Go historicky vynucovalo volbu mezi jednoduchostí sestavení a věrností enginu, která engineery stavěla do nepohodlné situace.

Na jedné straně stojí `github.com/mattn/go-sqlite3`, dlouhodobý komunitní standard. Přímo obaluje oficiální C amalgamaci SQLite prostřednictvím `cgo`. Získáváte skutečný C plánovač dotazů, plnou sémantiku souběžnosti WAL a předvídatelnou správu paměti v C. Cenou je `CGO_ENABLED=1`: lokální sestavení a CI pipeline se zpomalují, minimální scratch nebo distroless kontejnerové obrazy vyžadují odpovídající C běhové knihovny, křížová kompilace pro jiný operační systém nebo architekturu CPU vyžaduje kompletní C křížový toolchain a jakákoli poškozená paměť v C sdílí adresový prostor procesu Go.

Na druhé straně stojí čistě Go porty SQLite, vytvořené mechanickým překladem C amalgamace do zdrojového kódu Go nad emulovaným C runtime (`libc`). Obnovují `CGO_ENABLED=0`, což činí sestavení triviálním. Při dlouhodobé zátěži však přeložená C halda a alokace Go pro každý řádek zvyšují tlak na garbage collector a jemné okrajové případy při zamykání, časových limitech busy nebo přerušování příkazů se mohou odchylovat od nativních C sestavení.

Vytvořili jsme `gosuda.org/sqlite3`, aby Go aplikace nemusely volit mezi `CGO_ENABLED=0` a skutečným C enginem SQLite. Kompilací oficiální C amalgamace SQLite `3.54.1` do vyhrazeného serveru se sdílenou pamětí (`hqq-sqlite-server`) a komunikací přes `gosuda.org/hqq` se vaše Go binárka sestavuje s `CGO_ENABLED=0`, zatímco vykonává nezměněnou C SQLite optimalizovanou pomocí `-O3` napříč hranicí procesu.

## Konfigurace bez nastavení: od `go get` k `sql.Open`

Nahrazení `github.com/mattn/go-sqlite3` v existující Go kódové základně vyžaduje změnit pouze cestu k prázdnému importu. Ovladač se registruje jako `"sqlite3"` (a `"hqq-sqlite"` pro explicitní výběr) a přijímá stejné řetězce DSN:

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

### Vložené pracovní binárky s SLSA atestací

Přirozenou otázkou, když se dozvíte, že knihovna používá pracovní proces v C, je: _„Musím vedle svého Go programu instalovat nebo distribuovat samostatnou C binárku?“_

Ne. `gosuda.org/sqlite3` vkládá předkompilované binárky `hqq-sqlite-server` pro všech šest hlavních cílů přímo do souborů Go označených podle platformy (`internal/embedded/embed_<goos>_<goarch>.go`). Protože build constraints Go kompilují pouze soubor odpovídající vašemu cílovému `GOOS` a `GOARCH`, křížová kompilace příkazem `GOOS=linux GOARCH=arm64 go build` automaticky vloží pouze binárku serveru `linux/arm64` o velikosti přibližně 1,4 MiB.

| Cílový OS   | Cílová architektura (`GOARCH`) | Vložená binárka                       | Build toolchain a provenance                       |
| :---------- | :----------------------------- | :------------------------------------ | :------------------------------------------------- |
| **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  |

Když `sql.Open` naváže své první spojení, `autolaunch.go` materializuje vloženou binárku do uživatelského cache adresáře (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), ověří její kryptografický digest SHA-256 proti připnuté mapě `ReleaseHashes` v `hashes.go` prostřednictvím `gosuda.org/hqq/secureexec` a spustí pracovní proces s parametrem `--parent-stdin`, takže jeho životnost je přísně svázána s vaším Go procesem.

### Kompatibilita DSN s `mattn/go-sqlite3` v čistém provedení

Aby mohl fungovat jako skutečná náhrada bez nutnosti úprav, parser DSN (`dsn.go`) implementuje čistě navržený (clean-room) model parametrů připojení a pragma nastavení používaných napříč kódovými základnami `mattn/go-sqlite3`:

| Parametr DSN dotazu                                                                           | Přijímané hodnoty                                       | Účinek v SQLite                                                                                      |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Celé číslo v milisekundách (např. `5000`)               | Volá `sqlite3_busy_timeout(db, ms)` při otevření relace.                                             |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Provede `PRAGMA journal_mode = <mode>`.                                                              |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Vybírá `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` nebo `BEGIN DEFERRED` pro `db.BeginTx`.                  |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Provede `PRAGMA foreign_keys = ON/OFF`.                                                              |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Provede `PRAGMA synchronous = <level>`.                                                              |
| `_cache_size`                                                                                 | Celý počet stránek nebo `-KiB` (např. `-20000`)         | Provede `PRAGMA cache_size = <val>`.                                                                 |
| `_loc`                                                                                        | `auto` nebo IANA časové pásmo (např. `UTC`, `Asia/Seoul`) | Převádí textové sloupce `DATETIME`/`TIMESTAMP` na `*time.Time` v cílové lokalitě.                   |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Nastavuje `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` nebo `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Booleovská hodnota nebo celočíselný režim               | Aplikuje odpovídající relační `PRAGMA` při inicializaci připojení.                                   |

## Proměna izolace procesů ve výhodu propustnosti

Na první pohled se může zdát, že spouštění SQLite v odděleném procesu musí být pomalejší než volání `sqlite3_step` in-process přes `cgo`. V praxi se však jako první ukáže opak, jakmile dotaz vrátí více než jeden řádek.

### Skrytá cena volání `cgo` řádek po řádku

V konvenčním ovladači SQLite založeném na `cgo` `database/sql` řídí iteraci po jednom řádku a jednom sloupci. Čtení 1 000 řádků se čtyřmi sloupci vyvolá:

- 1 000 volání `cgo` na `sqlite3_step`
- 4 000 volání `cgo` na `sqlite3_column_type` a `sqlite3_column_int64` / `sqlite3_column_text`
- Tisíce jednotlivých přechodů hranic mezi plánovačem Go a zásobníkem C

### Pipelining požadavků a předběžné načítání dávek po 64 KiB

`gosuda.org/sqlite3` odstraňuje přechody hranic u každého řádku tím, že přepracovává protokol na úrovni drátu kolem dvou mechanismů v `stmt.go`, `rows.go` a `sqlite3/server/sqlite3_hooks.c`:

```text
Go klient (stmt.go / rows.go)                       C11 worker (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Pipeline v 1 letu:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    smyčka sqlite3_step() až 128 řádků
                                          <=======  Dávka 1 (řádky 1..128 zabalené ve slotu 64 KiB)
2. Okamžitě spustit předběžné načtení:
   FetchRowsAsync(128) (Dávka 2)          =======>  smyčka sqlite3_step() pro řádky 129..256
   Zatímco C krokuje Dávku 2, Go volající
   čte řádky 1..128 z Dávky 1!            <=======  Dávka 2 připravena ve sdílené paměti
```

1. **Pipelining dotazu v jednom letu (`stmt.go`)**: Když `QueryContext` nebo `Stmt.QueryContext` spustí příkaz pouze pro čtení, `s.query()` odešle `reset`, `bind_values`, `stmt_info` (při prvním spuštění) a `fetch_rows` asynchronně v jediném letu přes sdílenou paměť. Všechny čtyři operace se vykonávají za sebou na pracovním procesu C a vrátí se v jednom round tripu.
2. **Úzké dávkové smyčky v C a asynchronní předběžné načítání (`rows.go`)**: Uvnitř `fetch_rows` (`sqlite3_hooks.c`) pracovní proces C krokuje `sqlite3_step(stmt)` v nativní smyčce v C a kóduje až `FetchSize` řádků (výchozí 128) přímo do odpovědního slotu sdílené paměti o velikosti 64 KiB, dokud není dosažen limit dávky, slot se nezaplní nebo nenastane `SQLITE_DONE`. Jakmile `rows.ingest()` dekóduje Dávku 1, spustí `FetchRowsAsync` pro Dávku 2 ještě předtím, než vrátí první řádek volajícímu. Zatímco váš Go kód prochází Dávku 1, pracovní proces C již krokuje Dávku 2 na jiném jádru CPU.

Na Apple M4 (`darwin/arm64`) ukazuje srovnání `gosuda.org/sqlite3` (`CGO_ENABLED=0`) s `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) nad touže databází SQLite efekt dávkování a pipeliningu:

| Zátěž                               | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) |  `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Relativní rychlost                   |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Bodový výběr (`QueryRow` podle PK)** |            `3.52 µs/op` (9 B, 1 alokace) |       `3.56 µs/op` (232 B, 11 alokací) | **1.01x (parita, 11x méně alokací)** |
| **Čtení 100 řádků (`rows.Next`)**   |    `20.41 µs/op` (30.5 KB, 211 alokací) |   `36.34 µs/op` (21.8 KB, 309 alokací) | **1.78x rychlejší**                  |
| **Čtení 1 000 řádků (`rows.Next`)** |  `125.92 µs/op` (296 KB, 2 030 alokací) | `349.10 µs/op` (216 KB, 3 009 alokací) | **2.77x rychlejší**                  |
| **Připravený insert (autocommit)**  |           `5.33 µs/op` (0 B, 0 alokací) |        `5.88 µs/op` (120 B, 5 alokací) | **1.10x rychlejší (0 alokací)**      |
| **Dávka 100 řádků v transakci**     |         `189.99 µs/op` (16 B, 1 alokace) |  `191.50 µs/op` (12.1 KB, 503 alokací) | **1.01x (500x méně alokací)**        |

## Zrušení, sandboxing cest a obnova poolu připojení

Spouštění SQLite v izolovaném pracovním procesu také posiluje způsob, jakým ovladač zpracovává časové limity, nedůvěryhodné SQL a hranice souborového systému:

- **Deterministické zrušení kontextu (`conn.go` a `errors.go`)**: Každé volání ovladače aktivuje lehkou ochranu `context.AfterFunc` (`cn.guard`). Pokud je `context.Context` volajícího zrušen nebo jeho termín vyprší, zatímco na serveru běží pomalý rekurzivní dotaz nebo čekání na zámek, ochrana označí připojení jako chybné, ukončí lokální odkaz relace a odešle mimopásmový rámec `Cancel()`. Pracovní proces C okamžitě zavolá `sqlite3_interrupt(db)`, čímž uvolní `sqlite3_step`, zatímco ovladač Go mapuje výsledný stav `SQLITE_INTERRUPT` na `context.Canceled` nebo `context.DeadlineExceeded` a pokyne `database/sql`, aby nahradil připojení v poolu.
- **Sandboxing adresářů (`sqlsrv_pathrules.c`)**: Pracovní proces C vynucuje pravidla cest `--allow-dir` při každém volání `open_v2`, rozlišuje symbolické odkazy a odmítá relativní průchod `..` mimo povolený adresář. `sqlite3_load_extension` a `SQLITE_LIMIT_ATTACHED` jsou ve výchozím nastavení zakázány, aby SQL injection nemohla uniknout z adresáře databáze nebo načíst libovolné sdílené objekty.

## Struktura repozitáře

Repozitář je uspořádán tak, že hlavní balíček poskytuje ovladač `database/sql` pro přímou náhradu, zatímco `launcher/` a `sqlite3/` zůstávají přístupné pro pokročilá nasazení s vlastním serverem:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registrace ovladače database/sql ("sqlite3") a Connector API
├── dsn.go                     # Parser DSN mattn/go-sqlite3 v čistém provedení a konfigurace pragma
├── conn.go / tx.go            # Životní cyklus relace, ochrana kontextu a stavový automat transakcí
├── stmt.go / rows.go          # Pipelining požadavků v jednom letu a předběžné načítání dávek po 64 KiB
├── errors.go                  # Typované primární a rozšířené chybové kódy SQLite a shoda sentinelů
├── autolaunch.go / hashes.go  # Zero-config materializace vloženého serveru a připnutí SHA-256
├── internal/embedded/         # Binárky //go:embed označené platformou (linux, darwin, windows)
├── launcher/                  # Konfigurovatelný supervisor procesů pro vlastní nasazení serveru
└── sqlite3/                   # Generovaný hqq-bindgen Go klient a zdrojový kód serveru C11 SQLite
    └── server/                # Implementace serveru C11 (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Začínáme

Nainstalujte modul příkazem `go get` a otevřete databázi pomocí `sql.Open("sqlite3", dsn)`:

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

- **Repozitář na GitHubu**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Reference DSN a pragma**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Architektura a protokol na úrovni drátu**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
