---
id: 134c150de56c188c925476664026a1ff
author: Lemon Mint
title: 'gosuda.org/sqlite3: Driver database/sql dengan CGO_ENABLED=0 melalui IPC Shared-Memory'
description: Driver database/sql SQLite 3.54.1 yang dapat langsung digunakan (drop-in) untuk Go dengan CGO_ENABLED=0, isolasi worker memori bersama HQQ, prefetch baris batch sebesar 64 KiB, serta biner yang diattestasi SLSA.
language: id
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` adalah driver `database/sql` untuk Go dengan `CGO_ENABLED=0` yang menjalankan amalgamasi C SQLite `3.54.1` hulu di dalam proses pekerja terisolasi melalui ring memori bersama [HQQ](https://gosuda.org/hqq). Driver ini terdaftar dengan nama driver standar `"sqlite3"`, mendukung parameter query DSN dari `github.com/mattn/go-sqlite3`, menyertakan binary server prebuilt yang telah diattest SLSA untuk enam target sistem operasi dan arsitektur, serta menggunakan request pipelining dengan prefetch batch 64 KiB sehingga performanya melampaui `cgo` in-process pada query multi-baris.

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

## Mengapa Driver SQLite Go Lain?

SQLite merupakan salah satu perangkat lunak yang paling luas digunakan di dunia, namun penggunaannya dari Go secara historis memaksa para insinyur memilih antara kesederhanaan build dan fidelitas engine.

Di satu sisi terdapat `github.com/mattn/go-sqlite3`, standar komunitas yang telah lama digunakan. Driver ini membungkus amalgamasi resmi C SQLite secara langsung melalui `cgo`. Anda memperoleh query planner C yang sesungguhnya, semantik konkurensi WAL yang lengkap, dan manajemen memori C yang dapat diprediksi. Konsekuensinya adalah `CGO_ENABLED=1`: build lokal dan pipeline CI menjadi lebih lambat, container image scratch atau distroless minimal memerlukan pustaka runtime C yang sesuai, kompilasi silang ke sistem operasi atau arsitektur CPU lain memerlukan toolchain C lintas platform yang lengkap, dan setiap korupsi memori di sisi C berbagi ruang alamat proses Go.

Di sisi lain terdapat port SQLite murni Go yang dihasilkan dengan menerjemahkan amalgamasi C secara mekanis menjadi kode sumber Go di atas runtime C yang diemulasi (`libc`). Driver tersebut memulihkan `CGO_ENABLED=0`, sehingga build menjadi sederhana. Namun, pada beban kerja yang berkelanjutan, heap C hasil terjemahan dan alokasi Go per baris meningkatkan tekanan garbage collection, serta kasus-kasus tepi yang halus dalam penguncian, busy timeout, atau interupsi statement dapat berbeda dari build C native.

Kami membangun `gosuda.org/sqlite3` agar aplikasi Go tidak harus memilih antara `CGO_ENABLED=0` dan engine C SQLite yang sesungguhnya. Dengan mengompilasi amalgamasi C SQLite `3.54.1` resmi ke dalam server memori bersama khusus (`hqq-sqlite-server`) dan berkomunikasi melalui `gosuda.org/hqq`, binary Go Anda dikompilasi dengan `CGO_ENABLED=0` sambil tetap menjalankan C SQLite yang tidak dimodifikasi dan dioptimasi `-O3` melintasi batas proses.

## Eksekusi Tanpa Konfigurasi: Dari `go get` ke `sql.Open`

Mengganti `github.com/mattn/go-sqlite3` dalam basis kode Go yang ada hanya memerlukan perubahan pada path blank import. Driver terdaftar sebagai `"sqlite3"` (dan `"hqq-sqlite"` untuk pemilihan eksplisit) serta menerima string query DSN yang sama:

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

### Binary Pekerja Tersemat yang Diattest SLSA

Pertanyaan alami ketika mendengar bahwa sebuah library menggunakan proses pekerja C adalah: _"Apakah saya harus menginstal atau mengirimkan binary C terpisah bersama program Go saya?"_

Tidak. `gosuda.org/sqlite3` menyematkan binary `hqq-sqlite-server` prebuilt untuk keenam target utama secara langsung di dalam file Go bertag platform (`internal/embedded/embed_<goos>_<goarch>.go`). Karena build constraint Go hanya mengompilasi file yang sesuai dengan `GOOS` dan `GOARCH` target Anda, kompilasi silang `GOOS=linux GOARCH=arm64 go build` secara otomatis hanya menyematkan binary server `linux/arm64` berukuran sekitar 1,4 MiB.

| Sistem Operasi Target | Arsitektur Target (`GOARCH`) | Binary Tersemat                       | Toolchain Build & 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 |

Ketika `sql.Open` membuka koneksi pertamanya, `autolaunch.go` mengekstrak binary tersemat ke direktori cache pengguna (`$XDG_CACHE_HOME/hqq/bin/<sha256>/`), memverifikasi digest SHA-256 kriptografisnya terhadap map `ReleaseHashes` yang dipin di `hashes.go` melalui `gosuda.org/hqq/secureexec`, lalu menjalankan pekerja dengan `--parent-stdin` sehingga masa hidupnya terikat secara ketat pada proses Go Anda.

### Kompatibilitas DSN `mattn/go-sqlite3` dengan Implementasi Ulang Bersih

Agar dapat berfungsi sebagai pengganti langsung yang sesungguhnya, parser DSN (`dsn.go`) mengimplementasikan replika bersih dari knob koneksi dan pragma yang digunakan di berbagai basis kode `mattn/go-sqlite3`:

| Parameter Query DSN                                                                           | Nilai yang Diterima                                     | Efek SQLite                                                                                          |
| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------- |
| `_busy_timeout` / `_timeout`                                                                  | Integer milidetik (misalnya `5000`)                     | Memanggil `sqlite3_busy_timeout(db, ms)` saat sesi dibuka.                                           |
| `_journal_mode` / `_journal`                                                                  | `DELETE`, `TRUNCATE`, `PERSIST`, `MEMORY`, `WAL`, `OFF` | Mengeksekusi `PRAGMA journal_mode = <mode>`.                                                         |
| `_txlock`                                                                                     | `immediate`, `exclusive`, `deferred`                    | Memilih `BEGIN IMMEDIATE`, `BEGIN EXCLUSIVE`, atau `BEGIN DEFERRED` untuk `db.BeginTx`.              |
| `_foreign_keys` / `_fk`                                                                       | `on`, `true`, `1`, `off`, `false`, `0`                  | Mengeksekusi `PRAGMA foreign_keys = ON/OFF`.                                                         |
| `_synchronous` / `_sync`                                                                      | `0`/`OFF`, `1`/`NORMAL`, `2`/`FULL`, `3`/`EXTRA`        | Mengeksekusi `PRAGMA synchronous = <level>`.                                                         |
| `_cache_size`                                                                                 | Integer halaman atau `-KiB` (misalnya `-20000`)         | Mengeksekusi `PRAGMA cache_size = <val>`.                                                            |
| `_loc`                                                                                        | `auto` atau zona waktu IANA (misalnya `UTC`, `Asia/Seoul`) | Mengurai kolom teks `DATETIME`/`TIMESTAMP` menjadi `*time.Time` pada lokasi target.                  |
| `mode`                                                                                        | `ro`, `rw`, `rwc`, `memory`                             | Mengatur `SQLITE_OPEN_READONLY`, `SQLITE_OPEN_READWRITE`, `SQLITE_OPEN_CREATE`, atau `SQLITE_OPEN_MEMORY`. |
| `_case_sensitive_like`, `_query_only`, `_recursive_triggers`, `_secure_delete`, `_AutoVacuum` | Boolean atau integer mode                               | Menerapkan `PRAGMA` sesi yang sesuai saat inisialisasi koneksi.                                      |

## Mengubah Isolasi Proses Menjadi Keunggulan Throughput

Sekilas, menjalankan SQLite dalam proses terpisah tampak pasti lebih lambat daripada memanggil `sqlite3_step` secara in-process melalui `cgo`. Dalam praktiknya, justru yang terjadi sebaliknya begitu sebuah query mengembalikan lebih dari satu baris.

### Biaya Tersembunyi Pemanggilan `cgo` Baris demi Baris

Dalam driver SQLite `cgo` konvensional, `database/sql` melakukan iterasi satu baris dan satu kolom pada satu waktu. Membaca 1.000 baris dengan 4 kolom memicu:

- 1.000 pemanggilan `cgo` ke `sqlite3_step`
- 4.000 pemanggilan `cgo` ke `sqlite3_column_type` dan `sqlite3_column_int64` / `sqlite3_column_text`
- Ribuan lintasan batas individual antara scheduler Go dan stack C

### Request Pipelining dan Prefetch Batch 64 KiB

`gosuda.org/sqlite3` menghilangkan lintasan batas per baris dengan merancang ulang wire protocol di sekitar dua mekanisme dalam `stmt.go`, `rows.go`, dan `sqlite3/server/sqlite3_hooks.c`:

```text
Go Client (stmt.go / rows.go)                       C11 Worker (sqlite3_hooks.c)
-----------------------------                       ----------------------------
1. Pipeline dalam 1 flight:
   ResetAsync + BindValuesAsync
   + StmtInfoAsync + FetchRowsAsync(128)  =======>  reset -> bind -> stmt_info ->
                                                    loop sqlite3_step() hingga 128 baris
                                          <=======  Batch 1 (baris 1..128 dikemas dalam slot 64 KiB)
2. Segera jalankan prefetch:
   FetchRowsAsync(128) (Batch 2)          =======>  loop sqlite3_step() untuk baris 129..256
   Selagi C melakukan step Batch 2, pemanggil Go
   memindai baris 1..128 dari Batch 1!    <=======  Batch 2 siap di shared memory
```

1. **Single-Flight Query Pipelining (`stmt.go`)**: Ketika `QueryContext` atau `Stmt.QueryContext` menjalankan statement read-only, `s.query()` mengirimkan `reset`, `bind_values`, `stmt_info` (pada eksekusi pertama), dan `fetch_rows` secara asinkron dalam satu flight shared memory. Keempat operasi tersebut dieksekusi secara berurutan pada worker C dan dikembalikan dalam satu round trip.
2. **Loop Batch C yang Ketat dan Prefetch Asinkron (`rows.go`)**: Di dalam `fetch_rows` (`sqlite3_hooks.c`), worker C menjalankan `sqlite3_step(stmt)` dalam loop C native, mengodekan hingga `FetchSize` baris (default 128) langsung ke dalam slot respons shared memory 64 KiB hingga batas batch tercapai, slot penuh, atau `SQLITE_DONE` terjadi. Segera setelah `rows.ingest()` men-decode Batch 1, ia menjalankan `FetchRowsAsync` untuk Batch 2 sebelum mengembalikan baris pertama kepada pemanggil. Selagi kode Go Anda memindai Batch 1, worker C sudah melakukan step Batch 2 pada core CPU lain.

Pada Apple M4 (`darwin/arm64`), perbandingan `gosuda.org/sqlite3` (`CGO_ENABLED=0`) dengan `github.com/mattn/go-sqlite3` (`CGO_ENABLED=1`) pada basis data SQLite yang sama memperlihatkan dampak batching dan pipelining:

| Beban Kerja                         | `gosuda.org/sqlite3` (`CGO_ENABLED=0`) |  `mattn/go-sqlite3` (`CGO_ENABLED=1`) | Kecepatan Relatif                    |
| :---------------------------------- | -------------------------------------: | ------------------------------------: | :----------------------------------- |
| **Point Select (`QueryRow` berdasarkan PK)** |            `3,52 µs/op` (9 B, 1 alokasi) |       `3,56 µs/op` (232 B, 11 alokasi) | **1,01x (setara, 11x lebih sedikit alokasi)** |
| **Pemindaian 100 Baris (`rows.Next`)**      |    `20,41 µs/op` (30,5 KB, 211 alokasi) |   `36,34 µs/op` (21,8 KB, 309 alokasi) | **1,78x lebih cepat**                     |
| **Pemindaian 1.000 Baris (`rows.Next`)**    |  `125,92 µs/op` (296 KB, 2.030 alokasi) | `349,10 µs/op` (216 KB, 3.009 alokasi) | **2,77x lebih cepat**                     |
| **Prepared Insert (Autocommit)**    |           `5,33 µs/op` (0 B, 0 alokasi) |        `5,88 µs/op` (120 B, 5 alokasi) | **1,10x lebih cepat (0 alokasi)**          |
| **Batch Transaksi 100 Baris**         |         `189,99 µs/op` (16 B, 1 alokasi) |  `191,50 µs/op` (12,1 KB, 503 alokasi) | **1,01x (500x lebih sedikit alokasi)**        |

## Pembatalan, Sandboxing Path, dan Pemulihan Connection Pool

Menjalankan SQLite dalam worker terisolasi juga memperkuat cara driver menangani timeout, SQL yang tidak tepercaya, dan batas sistem berkas:

- **Pembatalan Context yang Deterministik (`conn.go` dan `errors.go`)**: Setiap pemanggilan driver memasang guard `context.AfterFunc` yang ringan (`cn.guard`). Jika `context.Context` pemanggil dibatalkan atau deadline-nya habis selagi query rekursif yang lambat atau lock wait sedang berjalan di server, guard menandai koneksi sebagai rusak, mematikan tautan sesi lokal, dan mengirimkan frame `Cancel()` out-of-band. Worker C segera memanggil `sqlite3_interrupt(db)`, membebaskan `sqlite3_step`, sementara driver Go memetakan status `SQLITE_INTERRUPT` yang dihasilkan menjadi `context.Canceled` atau `context.DeadlineExceeded` dan menginstruksikan `database/sql` untuk mengganti koneksi tersebut dalam pool.
- **Sandboxing Direktori (`sqlsrv_pathrules.c`)**: Worker C menerapkan aturan path `--allow-dir` pada setiap pemanggilan `open_v2`, me-resolve symlink dan menolak traversal `..` relatif ke luar direktori yang diizinkan. `sqlite3_load_extension` dan `SQLITE_LIMIT_ATTACHED` dinonaktifkan secara default sehingga SQL injection tidak dapat keluar dari direktori basis data atau memuat shared object sembarang.

## Tata Letak Repositori

Repositori disusun sedemikian rupa sehingga paket tingkat atas menyediakan driver `database/sql` yang dapat langsung digunakan, sementara `launcher/` dan `sqlite3/` tetap dapat diakses untuk deployment server kustom tingkat lanjut:

```text
gosuda.org/sqlite3
├── driver.go / api.go         # Registrasi driver database/sql ("sqlite3") dan API Connector
├── dsn.go                     # Parser DSN mattn/go-sqlite3 hasil implementasi ulang bersih dan konfigurasi pragma
├── conn.go / tx.go            # Siklus hidup sesi, context guard, dan state machine transaksi
├── stmt.go / rows.go          # Request pipelining single-flight dan prefetch batch 64 KiB
├── errors.go                  # Kode error primer/extended SQLite bertipe dan pencocokan sentinel
├── autolaunch.go / hashes.go  # Materialisasi server tersemat tanpa konfigurasi dan pinning SHA-256
├── internal/embedded/         # Binary //go:embed bertag platform (linux, darwin, windows)
├── launcher/                  # Supervisor proses yang dapat dikonfigurasi untuk deployment server kustom
└── sqlite3/                   # Klien Go hqq-bindgen yang dihasilkan dan source server C11 SQLite
    └── server/                # Implementasi server C11 (sqlite3_hooks.c, sqlsrv_policy.c)
```

## Memulai

Pasang modul dengan `go get` dan buka basis data Anda dengan `sql.Open("sqlite3", dsn)`:

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

- **Repositori GitHub**: [https://github.com/gosuda/hqq-sqlite3](https://github.com/gosuda/hqq-sqlite3)
- **Referensi DSN & Pragma**: [docs/DSN.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/DSN.md)
- **Arsitektur & Wire Protocol**: [docs/ARCHITECTURE.md](https://github.com/gosuda/hqq-sqlite3/blob/main/docs/ARCHITECTURE.md)
