---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: sterownik database/sql z CGO_ENABLED=0 oparty na Shared-Memory IPC'
description: Sterownik SQLite 3.54.1 dla Go zgodny z database/sql, z możliwością bezpośredniego zastąpienia, działający przy CGO_ENABLED=0, z izolacją workerów opartą na pamięci współdzielonej HQQ, pobieraniem wierszy wsadowo w porcjach po 64 KiB oraz plikami binarnymi poświadczonymi certyfikatem SLSA.
language: pl
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` to sterownik `database/sql` dla Go z ustawieniem `CGO_ENABLED=0`, który uruchamia upstreamowe amalgamation C SQLite `3.54.1` w odizolowanym procesie roboczym za pośrednictwem pierścieni pamięci współdzielonej [HQQ](https://gosuda.org/hqq). Rejestruje się pod standardową nazwą sterownika `"sqlite3"`, obsługuje parametry zapytań DSN znane z `github.com/mattn/go-sqlite3`, osadza dla sześciu systemów operacyjnych i architektur docelowych prekompilowane binaria serwera z atestacją SLSA oraz wykorzystuje potokowanie żądań i wstępne pobieranie wsadów po 64 KiB, dzięki czemu przewyższa wydajnością `cgo` działające w tym samym procesie przy zapytaniach zwracających wiele wierszy.

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

## Dlaczego kolejny sterownik SQLite dla Go?

SQLite jest jednym z najczęściej wdrażanych programów na świecie, jednak korzystanie z niego w Go historycznie zmuszało inżynierów do niewygodnego wyboru między prostotą budowania a wiernością silnika.

Z jednej strony stoi `github.com/mattn/go-sqlite3`, od dawna standard społeczności. Bezpośrednio opakowuje oficjalne amalgamation C SQLite za pośrednictwem `cgo`. Otrzymujemy prawdziwy planista zapytań C, pełną semantykę współbieżności WAL oraz przewidywalne zarządzanie pamięcią C. Kosztem jest `CGO_ENABLED=1`: lokalne buildy i potoki CI ulegają spowolnieniu, minimalne obrazy kontenerów typu scratch lub distroless wymagają odpowiednich bibliotek uruchomieniowych C, kompilacja krzyżowa na inny system operacyjny lub architekturę procesora wymaga pełnego kompletu narzędzi krzyżowych C, a każde uszkodzenie pamięci w kodzie C dzieli przestrzeń adresową procesu Go.

Z drugiej strony znajdują się czyste w Go porty SQLite, generowane przez mechaniczne tłumaczenie amalgamation C na kod źródłowy Go, działające na emulowanym środowisku uruchomieniowym C (`libc`). Przywracają one `CGO_ENABLED=0`, co czyni budowanie trywialnym. Przy długotrwałym obciążeniu przetłumaczony heap C oraz alokacje Go na każdy wiersz zwiększają jednak presję na garbage collector, a subtelne przypadki brzegowe w blokowaniu, limitach czasu oczekiwania (busy timeouts) czy przerywaniu instrukcji mogą odbiegać od natywnych buildów C.

Stworzyliśmy `gosuda.org/sqlite3`, aby aplikacje Go nie musiały wybierać między `CGO_ENABLED=0` a prawdziwym silnikiem C SQLite. Dzięki kompilowaniu oficjalnego amalgamation C SQLite `3.54.1` do dedykowanego serwera z pamięcią współdzieloną (`hqq-sqlite-server`) oraz komunikacji przez `gosuda.org/hqq`, Twoja binarka Go kompiluje się z `CGO_ENABLED=0`, jednocześnie wykonując niezmodyfikowany, zoptymalizowany flagą `-O3` kod C SQLite poza granicą procesu.

## Wykonanie bez konfiguracji: od `go get` do `sql.Open`

Zastąpienie `github.com/mattn/go-sqlite3` w istniejącej bazie kodu Go wymaga zmiany wyłącznie ścieżki importu z pustym identyfikatorem. Sterownik rejestruje się jako `"sqlite3"` (oraz `"hqq-sqlite"` dla jawnego wyboru) i akceptuje te same ciągi zapytań 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)
}
```

### Osadzone binaria roboczego z atestacją SLSA

Naturalnym pytaniem, gdy słyszy się, że biblioteka korzysta z procesu roboczego C, jest: _„Czy muszę zainstalować lub dostarczyć osobne binarium C obok mojego programu Go?”_

Nie. `gosuda.org/sqlite3` osadza prekompilowane binaria `hqq-sqlite-server` dla wszystkich sześciu głównych celów bezpośrednio w plikach Go oznaczonych platformą (`internal/embedded/embed_<goos>_<goarch>.go`). Ponieważ ograniczenia budowania (build constraints) Go kompilują wyłącznie plik odpowiadający Twojemu docelowemu `GOOS` i `GOARCH`, kompilacja krzyżowa `GOOS=linux GOARCH=arm64 go build` automatycznie osadza jedynie binarium serwera `linux/arm64` o rozmiarze około 1,4 MiB.

| System docelowy | Architektura docelowa (`GOARCH`) | Osadzone binarium                     | Zestaw narzędzi kompilacji i pochodzenie         |
| :-------------- | :------------------------------- | :------------------------------------ | :----------------------------------------------- |
| **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 |

Gdy `sql.Open` nawiązuje swoje pierwsze połączenie, `autolaunch.go` zapisuje osadzone binarium do katalogu cache użytkownika (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), weryfikuje jego kryptograficzny skrót SHA-256 względem przypiętej mapy `ReleaseHashes` w `hashes.go` za pośrednictwem `gosuda.org/hqq/secureexec` i uruchamia proces roboczy z opcją `--parent-stdin`, dzięki czemu jego czas życia jest ściśle powiązany z Twoim procesem Go.

### Kompatybilność DSN z `mattn/go-sqlite3` w modelu clean-room

Aby działać jako prawdziwy zamiennik typu drop-in, parser DSN (`dsn.go`) implementuje replikę clean-room parametrów połączenia i pragm stosowanych w bazach kodu `mattn/go-sqlite3`:

| Parametr zapytania DSN                                                                        | Akceptowane wartości                                    | Efekt w SQLite                                                                                       |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Liczba całkowita w milisekundach (np. `5000`)           | Wywołuje `sqlite3_busy_timeout(db, ms)` przy otwarciu sesji.                                         |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Wykonuje `PRAGMA journal_mode = <mode>`.                                                             |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Wybiera `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` lub `BEGIN DEFERRED` dla `db.BeginTx`.                  |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Wykonuje `PRAGMA foreign_keys = ON/OFF`.                                                             |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Wykonuje `PRAGMA synchronous = <level>`.                                                             |
| `_cache_size`                                                                                 | Liczba całkowita stron lub `-KiB` (np. `-20000`)        | Wykonuje `PRAGMA cache_size = <val>`.                                                                |
| `_loc`                                                                                        | `auto` lub strefa czasowa IANA (np. `UTC`, `Asia/Seoul`) | Parsuje kolumny tekstowe `DATETIME`/`TIMESTAMP` do `*time.Time` w docelowej lokalizacji.             |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Ustawia `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` lub `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Wartości logiczne lub liczby trybów                     | Stosuje odpowiedni `PRAGMA` sesji podczas inicjalizacji połączenia.                                  |

## Przekształcenie izolacji procesu w przewagę przepustowości

Na pierwszy rzut oka uruchamianie SQLite w osobnym procesie wydaje się musieć być wolniejsze niż wywoływanie `sqlite3_step` w tym samym procesie przez `cgo`. W praktyce dzieje się odwrotnie, gdy zapytanie zwraca więcej niż jeden wiersz.

### Ukryty koszt wywołań `cgo` wiersz po wierszu

W konwencjonalnym sterowniku SQLite opartym na `cgo`, `database/sql` prowadzi iterację po jednym wierszu i jednej kolumnie naraz. Odczyt 1000 wierszy z 4 kolumnami powoduje:

- 1000 wywołań `cgo` do `sqlite3_step`
- 4000 wywołań `cgo` do `sqlite3_column_type` oraz `sqlite3_column_int64` / `sqlite3_column_text`
- tysiące pojedynczych przekroczeń granic między harmonogramem Go a stosem C

### Potokowanie żądań i wstępne pobieranie wsadów po 64 KiB

`gosuda.org/sqlite3` eliminuje przekraczanie granic dla każdego wiersza, przeprojektowując protokół komunikacyjny wokół dwóch mechanizmów w `stmt.go`, `rows.go` oraz `sqlite3/server/sqlite3_hooks.c`:

```text
Klient Go (stmt.go / rows.go)                       Worker C11 (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Potok w 1 locie:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    pętla sqlite3_step() do 128 wierszy
                                          <=======  Wsad 1 (wiersze 1..128 spakowane w slocie 64 KiB)
2. Natychmiast uruchom prefetch:
   FetchRowsAsync(128) (Wsad 2)           =======>  pętla sqlite3_step() dla wierszy 129..256
   Podczas gdy C wykonuje kroki dla      
   Wsadu 2, wywołujący w Go odczytuje
   wiersze 1..128 z Wsadu 1!              <=======  Wsad 2 gotowy we współdzielonej pamięci
```

1. **Potokowanie zapytań w jednym locie (`stmt.go`)**: Gdy `QueryContext` lub `Stmt.QueryContext` uruchamia instrukcję tylko do odczytu, `s.query()` wysyła asynchronicznie `reset`, `bind_values`, `stmt_info` (przy pierwszym wykonaniu) oraz `fetch_rows` w jednym locie przez pamięć współdzieloną. Wszystkie cztery operacje wykonują się bez przerwy na workerze C i zwracają wynik w jednej rundzie komunikacyjnej.
2. **Ciasne pętle wsadowe w C oraz asynchroniczne wstępne pobieranie (`rows.go`)**: Wewnątrz `fetch_rows` (`sqlite3_hooks.c`) worker C wykonuje `sqlite3_step(stmt)` w natywnej pętli C, kodując bezpośrednio do slotu odpowiedzi w pamięci współdzielonej o rozmiarze 64 KiB do `FetchSize` wierszy (domyślnie 128), aż do osiągnięcia limitu wsadu, zapełnienia slotu lub wystąpienia `SQLITE_DONE`. Gdy tylko `rows.ingest()` zdekoduje Wsad 1, wysyła `FetchRowsAsync` dla Wsadu 2, zanim zwróci pierwszy wiersz wywołującemu. Podczas gdy Twój kod Go skanuje Wsad 1, worker C wykonuje już kroki dla Wsadu 2 na innym rdzeniu procesora.

Na Apple M4 (`darwin/arm64`), porównanie `gosuda.org/sqlite3` (`CGO_ENABLED=0`) z `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) na tej samej bazie SQLite pokazuje efekt wsadowania i potokowania:

| Obciążenie                          | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Względna szybkość                     |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :------------------------------------ |
| **Wybór punktowy (`QueryRow` po PK)** |            `3.52 µs/op` (9 B, 1 alokacja) |       `3.56 µs/op` (232 B, 11 alokacji) | **1,01x (parytet, 11x mniej alokacji)** |
| **Skan 100 wierszy (`rows.Next`)**  |    `20.41 µs/op` (30.5 KB, 211 alokacji) |   `36.34 µs/op` (21.8 KB, 309 alokacji) | **1,78x szybciej**                    |
| **Skan 1000 wierszy (`rows.Next`)** |  `125.92 µs/op` (296 KB, 2030 alokacji) | `349.10 µs/op` (216 KB, 3009 alokacji) | **2,77x szybciej**                    |
| **Wstawianie przygotowane (autocommit)** |           `5.33 µs/op` (0 B, 0 alokacji) |        `5.88 µs/op` (120 B, 5 alokacji) | **1,10x szybciej (0 alokacji)**       |
| **Wsadowa transakcja 100 wierszy**  |         `189.99 µs/op` (16 B, 1 alokacja) |  `191.50 µs/op` (12.1 KB, 503 alokacje) | **1,01x (500x mniej alokacji)**       |

## Anulowanie, piaskownica ścieżek i odzyskiwanie puli połączeń

Uruchamianie SQLite w odizolowanym procesie roboczym wzmacnia również sposób, w jaki sterownik obsługuje limity czasu, niezaufany SQL oraz granice systemu plików:

- **Deterministyczne anulowanie kontekstu (`conn.go` i `errors.go`)**: Każde wywołanie sterownika uzbraja lekki strażnik `context.AfterFunc` (`cn.guard`). Jeśli `context.Context` wywołującego zostanie anulowany lub jego termin wygaśnie, podczas gdy po stronie serwera trwa wolne rekurencyjne zapytanie lub oczekiwanie na blokadę, strażnik oznacza połączenie jako uszkodzone, zamyka lokalne łącze sesji i wysyła pozapasmową ramkę `Cancel()`. Worker C natychmiast wywołuje `sqlite3_interrupt(db)`, odblokowując `sqlite3_step`, a sterownik Go mapuje powstały status `SQLITE_INTERRUPT` na `context.Canceled` lub `context.DeadlineExceeded` i nakazuje `database/sql` zastąpić połączenie w puli.
- **Piaskownica katalogów (`sqlsrv_pathrules.c`)**: Worker C egzekwuje reguły ścieżek `--allow-dir` przy każdym wywołaniu `open_v2`, rozwiązując dowiązania symboliczne i odrzucając względne przejścia `..` poza dozwolony katalog. Funkcja `sqlite3_load_extension` oraz `SQLITE_LIMIT_ATTACHED` są domyślnie wyłączone, dzięki czemu wstrzyknięcie SQL nie może wyjść poza katalog bazy danych ani załadować dowolnych obiektów współdzielonych.

## Układ repozytorium

Repozytorium jest zorganizowane tak, że pakiet najwyższego poziomu udostępnia sterownik `database/sql` typu drop-in, podczas gdy `launcher/` i `sqlite3/` pozostają dostępne dla zaawansowanych wdrożeń z własnym serwerem:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Rejestracja sterownika database/sql ("sqlite3") i API Connector
├── dsn.go                     # Parser DSN mattn/go-sqlite3 w modelu clean-room i konfiguracja pragm
├── conn.go / tx.go            # Cykl życia sesji, strażnik kontekstu i maszyna stanów transakcji
├── stmt.go / rows.go          # Potokowanie żądań w jednym locie i wstępne pobieranie wsadów po 64 KiB
├── errors.go                  # Typowane podstawowe i rozszerzone kody błędów SQLite oraz dopasowanie wartości wartowniczych
├── autolaunch.go / hashes.go  # Materializacja osadzonego serwera bez konfiguracji i przypinanie SHA-256
├── internal/embedded/         # Binaria //go:embed oznaczone platformą (linux, darwin, windows)
├── launcher/                  # Konfigurowalny nadzorca procesów dla niestandardowych wdrożeń serwera
└── sqlite3/                   # Wygenerowany klient Go hqq-bindgen oraz kod źródłowy serwera C11 SQLite
    └── server/                # Implementacja serwera C11 (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Pierwsze kroki

Zainstaluj moduł poleceniem `go get` i otwórz bazę danych za pomocą `sql.Open("sqlite3", dsn)`:

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

- **Repozytorium GitHub**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Dokumentacja DSN i pragm**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Architektura i protokół komunikacyjny**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
