---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: Driver database/sql com CGO_ENABLED=0 sobre IPC de Memória Compartilhada'
description: Driver drop-in de database/sql para SQLite 3.54.1 em Go com CGO_ENABLED=0, isolamento de workers com shared memory HQQ, pré-busca de linhas em lotes de 64 KiB e binários atestados por SLSA.
language: pt
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` é um driver `database/sql` para Go com `CGO_ENABLED=0` que executa o amalgamation C oficial do SQLite `3.54.1` upstream dentro de um processo worker isolado, comunicando-se por meio de anéis de memória compartilhada [HQQ](https://gosuda.org/hqq). Ele se registra com o nome de driver padrão `"sqlite3"`, oferece suporte aos parâmetros de consulta do DSN de `github.com/mattn/go-sqlite3`, incorpora binários de servidor pré-compilados com atestação SLSA para seis alvos de sistema operacional e arquitetura, e utiliza pipelining de requisições com pré-busca de lotes de 64 KiB para superar o `cgo` em processo em consultas de múltiplas linhas.

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

## Por que Mais um Driver SQLite para Go?

O SQLite é um dos softwares mais implantados do mundo, porém, usá-lo a partir de Go historicamente obrigou os engenheiros a uma escolha incômoda entre simplicidade de build e fidelidade do motor.

De um lado está o `github.com/mattn/go-sqlite3`, o padrão consolidado da comunidade. Ele envolve diretamente o amalgamation C oficial do SQLite por meio de `cgo`. Obtém-se o planejador de consultas C real, a semântica completa de concorrência do WAL e o gerenciamento de memória C previsível. O custo é `CGO_ENABLED=1`: builds locais e pipelines de CI tornam-se mais lentos, imagens de contêiner mínimas (scratch ou distroless) exigem bibliotecas de runtime C correspondentes, a compilação cruzada para outro sistema operacional ou arquitetura de CPU requer um cross-toolchain C completo, e qualquer corrupção de memória no código C compartilha o espaço de endereçamento do processo Go.

Do outro lado estão os ports puros em Go do SQLite, gerados pela tradução mecânica do amalgamation C para código-fonte Go sobre um runtime C emulado (`libc`). Eles restauram `CGO_ENABLED=0`, o que torna os builds triviais. Sob cargas de trabalho sustentadas, entretanto, o heap C traduzido e as alocações por linha em Go aumentam a pressão sobre o coletor de lixo, e casos sutis de bloqueio, timeouts de ocupação ou interrupção de statements podem divergir das builds C nativas.

Construímos o `gosuda.org/sqlite3` para que aplicações Go não precisem escolher entre `CGO_ENABLED=0` e o motor C real do SQLite. Ao compilar o amalgamation C oficial do SQLite `3.54.1` em um servidor dedicado de memória compartilhada (`hqq-sqlite-server`) e comunicar-se por meio de `gosuda.org/hqq`, seu binário Go compila com `CGO_ENABLED=0` enquanto executa o SQLite em C não modificado e otimizado com `-O3` através de uma fronteira de processo.

## Execução Sem Configuração: Do `go get` ao `sql.Open`

Substituir o `github.com/mattn/go-sqlite3` em uma base de código Go existente exige alterar apenas o caminho do import anônimo. O driver se registra como `"sqlite3"` (e como `"hqq-sqlite"` para seleção explícita) e aceita as mesmas strings de consulta do 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)
}
```

### Binários Worker Incorporados e Atestados por SLSA

Uma pergunta natural ao saber que uma biblioteca utiliza um processo worker em C é: _"Preciso instalar ou distribuir um binário C separado junto ao meu programa Go?"_

Não. O `gosuda.org/sqlite3` incorpora binários `hqq-sqlite-server` pré-compilados para todos os seis alvos principais, diretamente em arquivos Go com tags de plataforma (`internal/embedded/embed_<goos>_<goarch>.go`). Como as restrições de build do Go compilam apenas o arquivo correspondente ao seu `GOOS` e `GOARCH` alvo, a compilação cruzada com `GOOS=linux GOARCH=arm64 go build` incorpora automaticamente apenas o binário de servidor `linux/arm64`, de aproximadamente 1,4 MiB.

| Sistema Operacional Alvo | Arquitetura Alvo (`GOARCH`) | Binário Incorporado                   | Toolchain de Build e Proveniência                 |
| :----------------------- | :-------------------------- | :------------------------------------ | :------------------------------------------------ |
| **Linux**                | `amd64`, `arm64`            | `hqq-sqlite-server_linux_<arch>`      | GCC `-O3`, Proveniência SLSA Build L3             |
| **macOS**                | `arm64`, `amd64`            | `hqq-sqlite-server_darwin_<arch>`     | AppleClang `-O3`, Proveniência SLSA Build L3      |
| **Windows**              | `amd64`                     | `hqq-sqlite-server_windows_amd64.exe` | MSVC `-O2`, Proveniência SLSA Build L3            |
| **Windows**              | `arm64`                     | `hqq-sqlite-server_windows_arm64.exe` | `zig cc -target aarch64-windows-gnu -O3`, SLSA L3 |

Quando o `sql.Open` estabelece sua primeira conexão, o `autolaunch.go` materializa o binário incorporado no diretório de cache do usuário (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), verifica seu digest SHA-256 criptográfico contra o mapa fixado `ReleaseHashes` em `hashes.go` por meio de `gosuda.org/hqq/secureexec`, e inicia o worker com `--parent-stdin`, de modo que seu tempo de vida fique estritamente vinculado ao seu processo Go.

### Compatibilidade de DSN com `mattn/go-sqlite3` por Implementação Independente (Clean-Room)

Para funcionar como um substituto direto genuíno, o analisador de DSN (`dsn.go`) implementa uma réplica clean-room dos parâmetros de conexão e de pragmas utilizados em bases de código `mattn/go-sqlite3`:

| Parâmetro de Consulta do DSN                                                                  | Valores Aceitos                                         | Efeito no SQLite                                                                                     |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Inteiro em milissegundos (ex.: `5000`)                  | Chama `sqlite3_busy_timeout(db, ms)` na abertura da sessão.                                          |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Executa `PRAGMA journal_mode = <mode>`.                                                              |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Seleciona `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE` ou `BEGIN DEFERRED` para `db.BeginTx`.                |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Executa `PRAGMA foreign_keys = ON/OFF`.                                                              |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Executa `PRAGMA synchronous = <level>`.                                                              |
| `_cache_size`                                                                                 | Inteiro em páginas ou `-KiB` (ex.: `-20000`)            | Executa `PRAGMA cache_size = <val>`.                                                                 |
| `_loc`                                                                                        | `auto` ou fuso horário IANA (ex.: `UTC`, `Asia/Seoul`)  | Converte colunas de texto `DATETIME`/`TIMESTAMP` em `*time.Time` no local de destino.                |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Define `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE` ou `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Booleano ou inteiros de modo                            | Aplica o `PRAGMA` de sessão correspondente na inicialização da conexão.                              |

## Transformando o Isolamento de Processo em Vantagem de Vazão

À primeira vista, executar o SQLite em um processo separado parece necessariamente mais lento do que chamar `sqlite3_step` em processo via `cgo`. Na prática, ocorre o oposto assim que uma consulta retorna mais de uma única linha.

### O Custo Oculto das Chamadas `cgo` Linha a Linha

Em um driver SQLite convencional baseado em `cgo`, o `database/sql` conduz a iteração uma linha e uma coluna por vez. Ler 1.000 linhas com 4 colunas dispara:

- 1.000 chamadas `cgo` a `sqlite3_step`
- 4.000 chamadas `cgo` a `sqlite3_column_type` e `sqlite3_column_int64` / `sqlite3_column_text`
- Milhares de travessias individuais de fronteira entre o escalonador do Go e a pilha C

### Pipelining de Requisições e Pré-busca de Lotes de 64 KiB

O `gosuda.org/sqlite3` elimina as travessias de fronteira por linha ao redesenhar o protocolo de comunicação em torno de dois mecanismos nos arquivos `stmt.go`, `rows.go` e `sqlite3/server/sqlite3_hooks.c`:

```text
Cliente Go (stmt.go / rows.go)                      Worker C11 (sqlite3_hooks.c)
------------------------------                      ----------------------------
1. Pipeline em 1 voo:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    loop sqlite3_step() até 128 linhas
                                          <=======  Lote 1 (linhas 1..128 empacotadas em slot de 64 KiB)
2. Disparar imediatamente a pré-busca:
   FetchRowsAsync(128) (Lote 2)           =======>  loop sqlite3_step() para as linhas 129..256
   Enquanto o C avança o Lote 2, o chamador
   Go lê as linhas 1..128 do Lote 1!      <=======  Lote 2 pronto na memória compartilhada
```

1. **Pipelining de Consulta em Voo Único (`stmt.go`)**: Quando `QueryContext` ou `Stmt.QueryContext` executa um statement somente leitura, `s.query()` submete `reset`, `bind_values`, `stmt_info` (na primeira execução) e `fetch_rows` de forma assíncrona em um único voo de memória compartilhada. Todas as quatro operações são executadas em sequência no worker C e retornam em uma única ida e volta.
2. **Laços Compactos de Lote em C e Pré-busca Assíncrona (`rows.go`)**: Dentro de `fetch_rows` (`sqlite3_hooks.c`), o worker C executa `sqlite3_step(stmt)` em um laço nativo em C, codificando até `FetchSize` linhas (padrão 128) diretamente no slot de resposta de 64 KiB da memória compartilhada, até que o limite do lote seja atingido, o slot esteja cheio ou ocorra `SQLITE_DONE`. Assim que o `rows.ingest()` decodifica o Lote 1, ele dispara `FetchRowsAsync` para o Lote 2 antes de devolver a primeira linha ao chamador. Enquanto seu código Go lê o Lote 1, o worker C já está avançando o Lote 2 em outro núcleo de CPU.

No Apple M4 (`darwin/arm64`), a comparação entre `gosuda.org/sqlite3` (`CGO_ENABLED=0`) e `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) sobre o mesmo banco de dados SQLite mostra o efeito do agrupamento em lotes e do pipelining:

| Carga de Trabalho                   | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) | `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Velocidade Relativa                  |
| :---------------------------------- | -------------------------------------: | -----------------------------------: | :----------------------------------- |
| **Seleção Pontual (`QueryRow` por PK)** |        `3,52 µs/op` (9 B, 1 alocação) |   `3,56 µs/op` (232 B, 11 alocações) | **1,01x (paridade, 11x menos alocações)** |
| **Leitura de 100 Linhas (`rows.Next`)** |  `20,41 µs/op` (30,5 KB, 211 alocações) | `36,34 µs/op` (21,8 KB, 309 alocações) | **1,78x mais rápido**                |
| **Leitura de 1.000 Linhas (`rows.Next`)** | `125,92 µs/op` (296 KB, 2.030 alocações) | `349,10 µs/op` (216 KB, 3.009 alocações) | **2,77x mais rápido**                |
| **Inserção Preparada (Autocommit)** |           `5,33 µs/op` (0 B, 0 alocações) |    `5,88 µs/op` (120 B, 5 alocações) | **1,10x mais rápido (0 alocações)**  |
| **Lote de Transação de 100 Linhas** |        `189,99 µs/op` (16 B, 1 alocação) | `191,50 µs/op` (12,1 KB, 503 alocações) | **1,01x (500x menos alocações)**     |

## Cancelamento, Isolamento de Caminhos e Recuperação do Pool de Conexões

Executar o SQLite em um worker isolado também fortalece a forma como o driver lida com timeouts, SQL não confiável e fronteiras do sistema de arquivos:

- **Cancelamento Determinístico de Contexto (`conn.go` e `errors.go`)**: Cada chamada do driver arma uma guarda leve `context.AfterFunc` (`cn.guard`). Se o `context.Context` do chamador for cancelado ou seu prazo expirar enquanto uma consulta recursiva lenta ou uma espera por bloqueio estiver em execução no servidor, a guarda marca a conexão como inválida, encerra o vínculo local de sessão e envia um quadro `Cancel()` fora de banda. O worker C invoca `sqlite3_interrupt(db)` imediatamente, desbloqueando `sqlite3_step`, enquanto o driver Go mapeia o status `SQLITE_INTERRUPT` resultante para `context.Canceled` ou `context.DeadlineExceeded` e instrui o `database/sql` a substituir a conexão no pool.
- **Sandbox de Diretórios (`sqlsrv_pathrules.c`)**: O worker C aplica as regras de caminho `--allow-dir` em toda chamada a `open_v2`, resolvendo links simbólicos e rejeitando travessias relativas `..` para fora do diretório permitido. `sqlite3_load_extension` e `SQLITE_LIMIT_ATTACHED` são desabilitados por padrão, de modo que injeção de SQL não possa escapar do diretório do banco de dados nem carregar objetos compartilhados arbitrários.

## Layout do Repositório

O repositório é estruturado de modo que o pacote de nível superior forneça o driver `database/sql` de substituição direta, enquanto `launcher/` e `sqlite3/` permanecem acessíveis para implantações avançadas com servidor personalizado:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registro do driver database/sql ("sqlite3") e API Connector
├── dsn.go                     # Analisador de DSN clean-room compatível com mattn/go-sqlite3 e configuração de pragmas
├── conn.go / tx.go            # Ciclo de vida da sessão, guarda de contexto e máquina de estados de transação
├── stmt.go / rows.go          # Pipelining de requisições em voo único e pré-busca de lotes de 64 KiB
├── errors.go                  # Códigos de erro primários/estendidos tipados do SQLite e correspondência de sentinelas
├── autolaunch.go / hashes.go  # Materialização zero-config do servidor incorporado e fixação SHA-256
├── internal/embedded/         # Binários //go:embed com tags de plataforma (linux, darwin, windows)
├── launcher/                  # Supervisor de processo configurável para implantações de servidor personalizado
└── sqlite3/                   # Cliente Go hqq-bindgen gerado e código-fonte do servidor SQLite C11
    └── server/                # Implementação do servidor C11 (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Primeiros Passos

Instale o módulo com `go get` e abra seu banco de dados com `sql.Open("sqlite3", dsn)`:

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

- **Repositório no GitHub**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Referência de DSN e Pragmas**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Arquitetura e Protocolo de Comunicação**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
