---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: CGO_ENABLED=0 database/sql Driver поверх Shared-Memory IPC'
description: Драйвер SQLite 3.54.1 для Go, совместимый с database/sql, с CGO_ENABLED=0, изоляцией рабочих процессов через разделяемую память HQQ, предварительной выборкой строк пакетами по 64 КиБ и бинарными файлами с аттестацией SLSA.
language: ru
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` — это драйвер `database/sql` для Go с `CGO_ENABLED=0`, который запускает upstream C-амальгаму SQLite `3.54.1` внутри изолированного рабочего процесса через разделяемые кольцевые буферы [HQQ](https://gosuda.org/hqq). Он регистрируется под стандартным именем драйвера `"sqlite3"`, поддерживает параметры строки запроса DSN из `github.com/mattn/go-sqlite3`, встраивает предварительно собранные серверные бинарные файлы с аттестацией SLSA для шести целевых комбинаций ОС и архитектуры и использует конвейеризацию запросов с предварительной выборкой пакетов по 64 КиБ, что позволяет превзойти встроенный `cgo` при выборке нескольких строк.

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

## Зачем еще один драйвер SQLite для Go?

SQLite является одним из самых широко используемых программных продуктов в мире, однако работа с ним из Go исторически ставила инженеров перед неудобным выбором между простотой сборки и точностью воспроизведения движка.

С одной стороны находится `github.com/mattn/go-sqlite3`, давний стандарт сообщества. Он напрямую оборачивает официальную C-амальгаму SQLite через `cgo`. Вы получаете настоящий планировщик запросов C, полную семантику конкурентности WAL и предсказуемое управление памятью C. Ценой этого является `CGO_ENABLED=1`: локальные сборки и CI-конвейеры замедляются, минимальные образы scratch или distroless требуют соответствующих библиотек времени выполнения C, кросс-компиляция для другой ОС или архитектуры процессора требует полного кросс-инструментария C, а любое повреждение памяти в C разделяет адресное пространство процесса Go.

С другой стороны находятся чисто Go-порты SQLite, созданные механической трансляцией C-амальгамы в исходный код Go поверх эмулируемой среды выполнения C (`libc`). Они восстанавливают `CGO_ENABLED=0`, что делает сборку тривиальной. Однако при длительных нагрузках транслированная куча C и выделения памяти для каждой строки в Go увеличивают нагрузку на сборщик мусора, а тонкие граничные случаи в блокировках, таймаутах ожидания занятости или прерывании операторов могут расходиться с нативными сборками C.

Мы создали `gosuda.org/sqlite3`, чтобы приложения на Go не были вынуждены выбирать между `CGO_ENABLED=0` и настоящим движком C SQLite. Компилируя официальную C-амальгаму SQLite `3.54.1` в выделенный сервер с разделяемой памятью (`hqq-sqlite-server`) и обмениваясь данными через `gosuda.org/hqq`, ваш бинарный файл Go собирается с `CGO_ENABLED=0`, при этом выполняя немодифицированный, оптимизированный с `-O3` C SQLite через границу процесса.

## Выполнение без конфигурации: от `go get` до `sql.Open`

Замена `github.com/mattn/go-sqlite3` в существующей кодовой базе Go требует изменения только пути пустого импорта. Драйвер регистрируется как `"sqlite3"` (а также `"hqq-sqlite"` для явного выбора) и принимает те же строки 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)
}
```

### Встроенные серверные бинарные файлы с аттестацией SLSA

Естественный вопрос, возникающий при знакомстве с библиотекой, которая использует рабочий процесс на C: _«Нужно ли устанавливать или поставлять отдельный бинарный файл C вместе с моей программой на Go?»_

Нет. `gosuda.org/sqlite3` встраивает предварительно собранные бинарные файлы `hqq-sqlite-server` для всех шести основных целевых платформ непосредственно в файлы Go с платформенными тегами (`internal/embedded/embed_<goos>_<goarch>.go`). Поскольку ограничения сборки Go компилируют только файл, соответствующий вашим целевым `GOOS` и `GOARCH`, кросс-компиляция `GOOS=linux GOARCH=arm64 go build` автоматически встраивает только серверный бинарный файл `linux/arm64` размером примерно 1,4 МиБ.

| Целевая ОС  | Целевая архитектура (`GOARCH`) | Встроенный бинарный файл              | Инструментарий сборки и происхождение             |
| :---------- | :----------------------------- | :------------------------------------ | :------------------------------------------------ |
| **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 |

Когда `sql.Open` устанавливает первое соединение, `autolaunch.go` материализует встроенный бинарный файл в пользовательский каталог кэша (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), проверяет его криптографическую контрольную сумму SHA-256 по закрепленной карте `ReleaseHashes` в `hashes.go` через `gosuda.org/hqq/secureexec` и запускает рабочий процесс с флагом `--parent-stdin`, чтобы его время жизни строго было связано с вашим процессом Go.

### Совместимость DSN с `mattn/go-sqlite3` (чистая реализация)

Чтобы работать как полноценная замена без изменений, анализатор DSN (`dsn.go`) реализует чистую (clean-room) копию параметров соединения и прагм, используемых в кодовых базах `mattn/go-sqlite3`:

| Параметр строки запроса DSN                                                                   | Допустимые значения                                     | Эффект в SQLite                                                                                      |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Целое число миллисекунд (например, `5000`)              | Вызывает `sqlite3_busy_timeout(db, ms)` при открытии сеанса.                                         |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Выполняет `PRAGMA journal_mode = <mode>`.                                                            |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Выбирает `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` или `BEGIN DEFERRED` для `db.BeginTx`.                 |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Выполняет `PRAGMA foreign_keys = ON/OFF`.                                                            |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Выполняет `PRAGMA synchronous = <level>`.                                                            |
| `_cache_size`                                                                                 | Целое число страниц или `-KiB` (например, `-20000`)     | Выполняет `PRAGMA cache_size = <val>`.                                                               |
| `_loc`                                                                                        | `auto` или часовой пояс IANA (например, `UTC`, `Asia/Seoul`) | Разбирает текстовые столбцы `DATETIME`/`TIMESTAMP` в `*time.Time` в целевой локации.            |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Устанавливает `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` или `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Логические значения или целочисленные режимы            | Применяет соответствующую сессионную `PRAGMA` при инициализации соединения.                          |

## Превращение изоляции процессов в преимущество пропускной способности

На первый взгляд запуск SQLite в отдельном процессе кажется заведомо более медленным, чем вызов `sqlite3_step` в том же процессе через `cgo`. На практике происходит обратное, как только запрос возвращает более одной строки.

### Скрытая стоимость вызовов `cgo` построчно

В обычном драйвере SQLite на основе `cgo` `database/sql` проходит по результатам строка за строкой и столбец за столбцом. Чтение 1 000 строк с 4 столбцами вызывает:

- 1 000 вызовов `cgo` для `sqlite3_step`
- 4 000 вызовов `cgo` для `sqlite3_column_type` и `sqlite3_column_int64` / `sqlite3_column_text`
- Тысячи отдельных переходов границ между планировщиком Go и стеком C

### Конвейеризация запросов и предварительная выборка пакетов по 64 КиБ

`gosuda.org/sqlite3` устраняет переходы границ для каждой строки, перестраивая протокол обмена вокруг двух механизмов в `stmt.go`, `rows.go` и `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. **Конвейеризация запроса в один полет (`stmt.go`)**: Когда `QueryContext` или `Stmt.QueryContext` выполняет оператор только для чтения, `s.query()` асинхронно отправляет `reset`, `bind_values`, `stmt_info` (при первом выполнении) и `fetch_rows` в одном полете через разделяемую память. Все четыре операции выполняются последовательно на рабочем процессе C и возвращаются за одно обращение.
2. **Плотные пакетные циклы C и асинхронная предварительная выборка (`rows.go`)**: Внутри `fetch_rows` (`sqlite3_hooks.c`) рабочий процесс C выполняет `sqlite3_step(stmt)` в нативном цикле C, кодируя до `FetchSize` строк (по умолчанию 128) непосредственно в слот ответа разделяемой памяти размером 64 КиБ, пока не будет достигнут предел пакета, слот не заполнится или не наступит `SQLITE_DONE`. Как только `rows.ingest()` декодирует Пакет 1, он запускает `FetchRowsAsync` для Пакета 2 до возврата первой строки вызывающему коду. Пока ваш код на Go сканирует Пакет 1, рабочий процесс C уже выполняет шаги для Пакета 2 на другом ядре процессора.

На Apple M4 (`darwin/arm64`) сравнение `gosuda.org/sqlite3` (`CGO_ENABLED=0`) с `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) на одной и той же базе данных SQLite показывает эффект пакетирования и конвейеризации:

| Нагрузка                            | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) |  `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Относительная скорость               |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Точечная выборка (`QueryRow` по PK)** |            `3.52 µs/op` (9 B, 1 alloc) |       `3.56 µs/op` (232 B, 11 allocs) | **1.01x (паритет, в 11 раз меньше выделений)** |
| **Сканирование 100 строк (`rows.Next`)**      |    `20.41 µs/op` (30.5 KB, 211 allocs) |   `36.34 µs/op` (21.8 KB, 309 allocs) | **в 1.78 раза быстрее**                     |
| **Сканирование 1 000 строк (`rows.Next`)**    |  `125.92 µs/op` (296 KB, 2,030 allocs) | `349.10 µs/op` (216 KB, 3,009 allocs) | **в 2.77 раза быстрее**                     |
| **Подготовленная вставка (автокоммит)**    |           `5.33 µs/op` (0 B, 0 allocs) |        `5.88 µs/op` (120 B, 5 allocs) | **в 1.10 раза быстрее (0 allocs)**          |
| **Пакетная транзакция из 100 строк**         |         `189.99 µs/op` (16 B, 1 alloc) |  `191.50 µs/op` (12.1 KB, 503 allocs) | **1.01x (в 500 раз меньше выделений)**        |

## Отмена, изоляция путей и восстановление пула соединений

Выполнение SQLite в изолированном рабочем процессе также усиливает то, как драйвер обрабатывает тайм-ауты, недоверенный SQL и границы файловой системы:

- **Детерминированная отмена контекста (`conn.go` и `errors.go`)**: Каждый вызов драйвера активирует легковесный защитный механизм `context.AfterFunc` (`cn.guard`). Если `context.Context` вызывающего кода отменяется или истекает его срок, пока на сервере выполняется медленный рекурсивный запрос или ожидание блокировки, защитный механизм помечает соединение как неисправное, разрывает локальное сеансовое соединение и отправляет внеполосный кадр `Cancel()`. Рабочий процесс C немедленно вызывает `sqlite3_interrupt(db)`, снимая блокировку `sqlite3_step`, а драйвер Go сопоставляет полученный статус `SQLITE_INTERRUPT` с `context.Canceled` или `context.DeadlineExceeded` и указывает `database/sql` заменить соединение в пуле.
- **Изоляция каталогов (`sqlsrv_pathrules.c`)**: Рабочий процесс C применяет правила путей `--allow-dir` при каждом вызове `open_v2`, разрешая символические ссылки и отклоняя относительный обход `..` за пределы разрешенного каталога. `sqlite3_load_extension` и `SQLITE_LIMIT_ATTACHED` по умолчанию отключены, поэтому SQL-инъекция не может выйти за пределы каталога базы данных или загрузить произвольные разделяемые объекты.

## Структура репозитория

Репозиторий устроен так, что пакет верхнего уровня предоставляет драйвер `database/sql` для прямой замены, тогда как `launcher/` и `sqlite3/` остаются доступными для продвинутых развертываний с пользовательским сервером:

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

## Начало работы

Установите модуль с помощью `go get` и откройте базу данных через `sql.Open("sqlite3", dsn)`:

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

- **Репозиторий GitHub**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Справочник по DSN и прагмам**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Архитектура и протокол обмена**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
