puregosqlite

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 26, 2026 License: MIT Imports: 0 Imported by: 0

README

purego-sqlite

Go bindings for SQLite via purego instead of cgo.

Linux only. CGO_ENABLED=0.

[!WARNING] This is early stage. The full SQLite C API (~350 functions) is bound, but the database/sql driver covers the common path only. API may change.

Architecture

The binding layer is generated from sqlite3.h. The generator parses the system header and produces:

  • purego function bindings for all ~350 non-variadic SQLite functions
  • Typed SyscallN wrappers for variadic functions at known arities
  • Outbound port interfaces grouped by API surface (Lifecycle, Prepare, Column, Bind, etc.)
  • A composite CAPI interface embedding all port groups
  • Inbound port interfaces (DB, Stmt, Rows, Result) for the public API
  • Public API composition root wiring everything together

A hand-written core domain (~500 LOC) implements the business logic: open, prepare, bind, step, scan, close. A thin database/sql/driver adapter sits on top.

Mocks for all interfaces are generated by Mockery v3 and shipped in sqlite/mocks/.

Usage

database/sql driver
import (
    "database/sql"
    _ "github.com/bnema/purego-sqlite/driver"
)

db, err := sql.Open("sqlite3", "./my.db")
Direct API
import "github.com/bnema/purego-sqlite/sqlite"

db, err := sqlite.Open("./my.db")
defer db.Close()

stmt, _ := db.Prepare("SELECT name FROM users WHERE id = ?")
rows, _ := stmt.Query(42)
Testing with mocks

The package ships pre-generated Mockery v3 mocks for all public interfaces. Your code accepts sqlite.DB (an interface), and in tests you swap in a mock — no libsqlite3.so needed.

Step 1: Accept the interface in your code

package userrepo

import "github.com/bnema/purego-sqlite/sqlite"

type UserRepo struct {
    db sqlite.DB
}

func New(db sqlite.DB) *UserRepo {
    return &UserRepo{db: db}
}

func (r *UserRepo) GetName(id int) (string, error) {
    rows, err := r.db.Query("SELECT name FROM users WHERE id = ?", id)
    if err != nil {
        return "", err
    }
    defer rows.Close()
    if !rows.Next() {
        return "", fmt.Errorf("user %d not found", id)
    }
    var name string
    if err := rows.Scan(&name); err != nil {
        return "", err
    }
    return name, nil
}

Step 2: Mock it in tests

package userrepo_test

import (
    "testing"

    "github.com/bnema/purego-sqlite/sqlite/mocks"
    "github.com/stretchr/testify/require"
)

func TestGetName(t *testing.T) {
    // Create mocks — expectations are auto-asserted on cleanup
    mockDB := mocks.NewMockDB(t)
    mockRows := mocks.NewMockRows(t)

    // Set up the call chain
    mockDB.EXPECT().
        Query("SELECT name FROM users WHERE id = ?", 42).
        Return(mockRows, nil)
    mockRows.EXPECT().Next().Return(true)
    mockRows.EXPECT().Scan().RunAndReturn(func(dest ...any) error {
        *(dest[0].(*string)) = "Alice"
        return nil
    })
    mockRows.EXPECT().Close().Return(nil)

    // Test
    repo := userrepo.New(mockDB)
    name, err := repo.GetName(42)
    require.NoError(t, err)
    require.Equal(t, "Alice", name)
}

Available mocks in github.com/bnema/purego-sqlite/sqlite/mocks:

Mock Interface Key methods
MockDB sqlite.DB Prepare, Exec, Query, Close
MockStmt sqlite.Stmt Exec, Query, NumInput, Close
MockRows sqlite.Rows Next, Scan, Columns, Err, Close
MockResult sqlite.Result LastInsertId, RowsAffected

All mocks support .EXPECT() for type-safe expectations and .On()/.Return() for classic testify style.

Note: The driver registers as sqlite3, the same name as mattn/go-sqlite3. Do not import both in the same binary.

Requirements

  • Go 1.23+
  • libsqlite3.so on the system (present on virtually all Linux distros)
  • Override path: SQLITE_LIB_PATH=/custom/path/libsqlite3.so

Building

CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go test ./...

Integration tests need libsqlite3.so:

go test -tags integration ./integration/

Regenerating bindings

go generate ./...

Or manually:

go run ./cmd/sqlitegen --header /usr/include/sqlite3.h --output-dir .
mockery

Project layout

sqlite/              public API (generated) + mocks
driver/              database/sql adapter (hand-written)
internal/
  ports/in/          inbound port interfaces (generated)
  ports/out/         outbound port interfaces (generated) + mocks
  core/              domain logic: open, prepare, bind, step, scan (hand-written)
  capi/              purego bindings + bridge (generated + hand-written)
  loader/            dlopen libsqlite3.so (hand-written)
cmd/sqlitegen/       binding generator (parser, model, emitter, templates)
integration/         integration tests (require libsqlite3.so)

License

MIT

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
sqlitegen command
Command sqlitegen generates Go bindings for sqlite3.h using purego.
Command sqlitegen generates Go bindings for sqlite3.h using purego.
sqlitegen/internal/parser
Package parser extracts SQLITE_API function declarations, #define constants, and typedef structs from sqlite3.h.
Package parser extracts SQLITE_API function declarations, #define constants, and typedef structs from sqlite3.h.
internal
capi
Code generated by sqlitegen.
Code generated by sqlitegen.
ports/in
Code generated by sqlitegen.
Code generated by sqlitegen.
ports/out
Code generated by sqlitegen.
Code generated by sqlitegen.
Code generated by sqlitegen.
Code generated by sqlitegen.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL