---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: Driver database/sql cu CGO_ENABLED=0 prin IPC bazat pe memorie partajată'
description: Driver database/sql compatibil pentru SQLite 3.54.1 destinat Go, cu CGO_ENABLED=0, izolarea worker-ilor prin memorie partajată HQQ, prefetch-ul rândurilor în loturi de 64 KiB și binare atestate SLSA.
language: ro
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` este un driver `database/sql` pentru Go, cu `CGO_ENABLED=0`, care rulează amalgamarea C SQLite `3.54.1` din upstream într-un proces worker izolat, prin inele de memorie partajată [HQQ](https://gosuda.org/hqq). Se înregistrează sub numele standard de driver `"sqlite3"`, acceptă parametrii de interogare DSN din `github.com/mattn/go-sqlite3`, încorporează binare de server precompilate, atestate SLSA, pentru șase ținte de sistem de operare și arhitectură, și utilizează pipelining al cererilor și prefetch de loturi de 64 KiB pentru a depăși `cgo` în proces la interogările cu mai multe rânduri.

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

## De ce încă un driver SQLite pentru Go?

SQLite este unul dintre cele mai utilizate produse software din lume, totuși utilizarea sa din Go a obligat, în mod tradițional, inginerii să aleagă între simplitatea compilării și fidelitatea motorului.

Pe de o parte se află `github.com/mattn/go-sqlite3`, standardul comunitar de lungă durată. Acesta încapsulează direct amalgamarea oficială C SQLite prin `cgo`. Obțineți planificatorul real de interogări C, semantica completă de concurență WAL și gestionarea predictibilă a memoriei C. Costul constă în `CGO_ENABLED=1`: compilările locale și pipeline-urile CI se încetinesc, imaginile de container minimale (scratch sau distroless) necesită biblioteci runtime C compatibile, compilarea încrucișată către alt sistem de operare sau altă arhitectură CPU necesită un lanț complet de instrumente C pentru compilare încrucișată, iar orice coruperea memoriei în C împarte spațiul de adrese al procesului Go.

Pe de altă parte există porturi SQLite pur Go, generate prin traducerea mecanică a amalgamării C în cod sursă Go, peste un runtime C emulat (`libc`). Acestea restabilesc `CGO_ENABLED=0`, ceea ce face compilarea banală. În sarcini susținute, totuși, heap-ul C tradus și alocările Go pentru fiecare rând sporesc presiunea colectării de gunoi, iar cazuri-limită subtile în blocare, în timeout-uri de ocupare sau în întreruperea instrucțiunilor pot diverge de la compilările C native.

Am construit `gosuda.org/sqlite3` pentru ca aplicațiile Go să nu fie nevoite să aleagă între `CGO_ENABLED=0` și motorul real C SQLite. Prin compilarea amalgamării C oficiale SQLite `3.54.1` într-un server dedicat cu memorie partajată (`hqq-sqlite-server`) și prin comunicarea prin `gosuda.org/hqq`, binarul dumneavoastră Go se compilează cu `CGO_ENABLED=0`, executând în același timp SQLite C nemodificat, optimizat cu `-O3`, peste o frontieră de proces.

## Execuție fără configurare: de la `go get` la `sql.Open`

Înlocuirea `github.com/mattn/go-sqlite3` într-o bază de cod Go existentă necesită modificarea doar a căii de import anonim. Driverul se înregistrează ca `"sqlite3"` (și ca `"hqq-sqlite"` pentru selecție explicită) și acceptă aceleași șiruri de interogare 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)
}
```

### Binare worker încorporate, atestate SLSA

O întrebare firească, atunci când aflați că o bibliotecă utilizează un proces worker C, este: _„Trebuie să instalez sau să livrez un binar C separat alături de programul meu Go?”_

Nu. `gosuda.org/sqlite3` încorporează binare `hqq-sqlite-server` precompilate pentru toate cele șase ținte principale, direct în fișiere Go etichetate pe platformă (`internal/embedded/embed_<goos>_<goarch>.go`). Deoarece constrângerile de build ale Go compilează doar fișierul corespunzător țintei dumneavoastră `GOOS` și `GOARCH`, compilarea încrucișată `GOOS=linux GOARCH=arm64 go build` încorporează automat doar binarul de server `linux/arm64`, de aproximativ 1,4 MiB.

| Sistem de operare țintă | Arhitectură țintă (`GOARCH`) | Binar încorporat                      | Lanț de compilare și proveniență                  |
| :---------------------- | :--------------------------- | :------------------------------------ | :------------------------------------------------ |
| **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 |

Când `sql.Open` stabilește prima conexiune, `autolaunch.go` materializează binarul încorporat în directorul cache al utilizatorului (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifică digestul criptografic SHA-256 al acestuia în raport cu harta fixată `ReleaseHashes` din `hashes.go`, prin intermediul `gosuda.org/hqq/secureexec`, și lansează worker-ul cu `--parent-stdin`, astfel încât durata sa de viață să fie strict legată de procesul dumneavoastră Go.

### Compatibilitate DSN `mattn/go-sqlite3` realizată prin reimplementare independentă

Pentru a funcționa ca un înlocuitor real, fără modificări, parserul DSN (`dsn.go`) implementează o replică realizată prin reimplementare independentă a parametrilor de conexiune și de pragma utilizați în bazele de cod `mattn/go-sqlite3`:

| Parametru de interogare DSN                                                                   | Valori acceptate                                        | Efect SQLite                                                                                         |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Număr întreg de milisecunde (ex. `5000`)                | Apelează `sqlite3_busy_timeout(db, ms)` la deschiderea sesiunii.                                     |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Execută `PRAGMA journal_mode = <mode>`.                                                              |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Selectează `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` sau `BEGIN DEFERRED` pentru `db.BeginTx`.            |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Execută `PRAGMA foreign_keys = ON/OFF`.                                                              |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Execută `PRAGMA synchronous = <level>`.                                                              |
| `_cache_size`                                                                                 | Număr întreg de pagini sau `-KiB` (ex. `-20000`)        | Execută `PRAGMA cache_size = <val>`.                                                                 |
| `_loc`                                                                                        | `auto` sau fus orar IANA (ex. `UTC`, `Asia/Seoul`)      | Analizează coloanele text `DATETIME`/`TIMESTAMP` și le convertește în `*time.Time` în locația țintă. |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Setează `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` sau `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Valori booleene sau numere întregi de mod              | Aplică `PRAGMA` de sesiune corespunzător la inițializarea conexiunii.                                |

## Transformarea izolării proceselor într-un avantaj de debit

La prima vedere, rularea SQLite într-un proces separat pare să fie obligatoriu mai lentă decât apelarea `sqlite3_step` în proces prin `cgo`. În practică, se întâmplă exact opusul de îndată ce o interogare returnează mai mult de un singur rând.

### Costul ascuns al apelurilor `cgo` rând cu rând

Într-un driver SQLite convențional bazat pe `cgo`, `database/sql` parcurge rezultatele câte un rând și câte o coloană. Citirea a 1.000 de rânduri cu 4 coloane declanșează:

- 1.000 de apeluri `cgo` către `sqlite3_step`
- 4.000 de apeluri `cgo` către `sqlite3_column_type` și `sqlite3_column_int64` / `sqlite3_column_text`
- mii de treceri individuale ale frontierei între planificatorul Go și stiva C

### Pipelining al cererilor și prefetch de loturi de 64 KiB

`gosuda.org/sqlite3` elimină trecerile de frontieră pe rând prin reproiectarea protocolului de transport în jurul a două mecanisme din `stmt.go`, `rows.go` și `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. **Pipelining al interogărilor pe un singur zbor (`stmt.go`)**: Când `QueryContext` sau `Stmt.QueryContext` rulează o instrucțiune doar pentru citire, `s.query()` trimite asincron `reset`, `bind_values`, `stmt_info` (la prima execuție) și `fetch_rows` într-un singur zbor prin memoria partajată. Toate cele patru operații se execută una după alta pe worker-ul C și revin într-o singură rundă de comunicare.
2. **Bucle compacte de lot în C și prefetch asincron (`rows.go`)**: În interiorul `fetch_rows` (`sqlite3_hooks.c`), worker-ul C execută `sqlite3_step(stmt)` într-o buclă C nativă, codificând până la `FetchSize` rânduri (implicit 128) direct în slotul de răspuns de 64 KiB din memoria partajată, până la atingerea limitei de lot, până la umplerea slotului sau până la apariția `SQLITE_DONE`. Imediat ce `rows.ingest()` decodifică Lotul 1, trimite `FetchRowsAsync` pentru Lotul 2, înainte de a returna primul rând apelantului. Cât timp codul dumneavoastră Go parcurge Lotul 1, worker-ul C execută deja pașii pentru Lotul 2 pe un alt nucleu CPU.

Pe Apple M4 (`darwin/arm64`), compararea `gosuda.org/sqlite3` (`CGO_ENABLED=0`) cu `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) pe aceeași bază de date SQLite arată efectul grupării în loturi și al pipelining-ului:

| Sarcină de lucru                    | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Viteză relativă                       |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Point Select (`QueryRow` după PK)** |            `3.52 µs/op` (9 B, 1 alocare) |       `3.56 µs/op` (232 B, 11 alocări) | **1.01x (paritate, cu 11x mai puține alocări)** |
| **Parcurgere de 100 de rânduri (`rows.Next`)**      |    `20.41 µs/op` (30.5 KB, 211 alocări) |   `36.34 µs/op` (21.8 KB, 309 alocări) | **1.78x mai rapid**                     |
| **Parcurgere de 1.000 de rânduri (`rows.Next`)**    |  `125.92 µs/op` (296 KB, 2.030 alocări) | `349.10 µs/op` (216 KB, 3.009 alocări) | **2.77x mai rapid**                     |
| **Insert pregătit (Autocommit)**    |           `5.33 µs/op` (0 B, 0 alocări) |        `5.88 µs/op` (120 B, 5 alocări) | **1.10x mai rapid (0 alocări)**          |
| **Lot de tranzacție de 100 de rânduri**         |         `189.99 µs/op` (16 B, 1 alocare) |  `191.50 µs/op` (12.1 KB, 503 alocări) | **1.01x (de 500x mai puține alocări)**        |

## Anulare, sandboxing al căilor și recuperarea pool-ului de conexiuni

Rularea SQLite într-un worker izolat consolidează și modul în care driverul gestionează timeout-urile, SQL-ul nesigur și frontierele sistemului de fișiere:

- **Anulare deterministă a contextului (`conn.go` și `errors.go`)**: Fiecare apel al driverului armează o gardă ușoară `context.AfterFunc` (`cn.guard`). Dacă `context.Context` al apelantului este anulat sau își expiră termenul în timp ce o interogare recursivă lentă sau o așteptare pentru blocare rulează pe server, garda marchează conexiunea ca defectuoasă, închide legătura locală de sesiune și trimite un cadru `Cancel()` out-of-band. Worker-ul C invocă imediat `sqlite3_interrupt(db)`, deblocând `sqlite3_step`, în timp ce driverul Go mapează statusul rezultat `SQLITE_INTERRUPT` la `context.Canceled` sau `context.DeadlineExceeded` și îi cere `database/sql` să înlocuiască conexiunea în pool.
- **Sandboxing al directoarelor (`sqlsrv_pathrules.c`)**: Worker-ul C aplică regulile de cale `--allow-dir` la fiecare apel `open_v2`, rezolvând legăturile simbolice și respingând traversarea relativă `..` în afara directorului permis. `sqlite3_load_extension` și `SQLITE_LIMIT_ATTACHED` sunt dezactivate implicit, astfel încât injecția SQL nu poate scăpa din directorul bazei de date sau nu poate încărca obiecte partajate arbitrare.

## Structura depozitului

Depozitul este structurat astfel încât pachetul de nivel superior să furnizeze driverul `database/sql` gata de înlocuire, în timp ce `launcher/` și `sqlite3/` rămân accesibile pentru implementări avansate cu server personalizat:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # database/sql driver registration ("sqlite3") and Connector API
├── dsn.go                     # Clean-room mattn/go-sqlite3 DSN parser and pragma configuration
├── conn.go / tx.go            # Session lifecycle, context guard, and transaction state machine
├── stmt.go / rows.go          # Single-flight request pipelining and 64 KiB batch prefetching
├── errors.go                  # Typed SQLite primary/extended error codes and sentinel matching
├── autolaunch.go / hashes.go  # Zero-config embedded server materialization and SHA-256 pinning
├── internal/embedded/         # Platform-tagged //go:embed binaries (linux, darwin, windows)
├── launcher/                  # Configurable process supervisor for custom server deployments
└── sqlite3/                   # Generated hqq-bindgen Go client and C11 SQLite server source
    └── server/                # C11 server implementation (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Primii pași

Instalați modulul cu `go get` și deschideți baza de date cu `sql.Open("sqlite3", dsn)`:

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

- **Depozit GitHub**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Referință DSN și pragma**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Arhitectură și protocol de transport**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
