Files
2026-04-17 23:26:01 +00:00

4.5 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use DBConnect Only When You Need a Custom SQLite Driver MEDIUM Incorrect driver setup breaks both data.db and auxiliary.db, or introduces unnecessary CGO go, extending, sqlite, custom-driver, cgo, fts5, dbconnect

Use DBConnect Only When You Need a Custom SQLite Driver

PocketBase ships with the pure-Go modernc.org/sqlite driver (no CGO required). Only reach for a custom driver when you specifically need SQLite extensions like ICU, FTS5, or spatialite that the default driver doesn't expose. DBConnect is called twice — once for pb_data/data.db and once for pb_data/auxiliary.db — so driver registration and PRAGMAs must be idempotent.

Incorrect (unnecessary custom driver, mismatched builder, CGO without justification):

// ❌ Adding a CGO dependency with no need for extensions
import _ "github.com/mattn/go-sqlite3"

func main() {
    app := pocketbase.NewWithConfig(pocketbase.Config{
        DBConnect: func(dbPath string) (*dbx.DB, error) {
            // ❌ "sqlite3" builder name used but "pb_sqlite3" driver was registered —
            //    or vice versa — causing "unknown driver" / broken query generation
            return dbx.Open("sqlite3", dbPath)
        },
    })
    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

Correct (mattn/go-sqlite3 with CGO — proper PRAGMA init hook and builder map entry):

package main

import (
    "database/sql"
    "log"

    "github.com/mattn/go-sqlite3"
    "github.com/pocketbase/dbx"
    "github.com/pocketbase/pocketbase"
)

func init() {
    // Use a unique driver name to avoid conflicts with other packages.
    // sql.Register panics if called twice with the same name, so put it in init().
    sql.Register("pb_sqlite3", &sqlite3.SQLiteDriver{
        ConnectHook: func(conn *sqlite3.SQLiteConn) error {
            _, err := conn.Exec(`
                PRAGMA busy_timeout      = 10000;
                PRAGMA journal_mode      = WAL;
                PRAGMA journal_size_limit = 200000000;
                PRAGMA synchronous       = NORMAL;
                PRAGMA foreign_keys      = ON;
                PRAGMA temp_store        = MEMORY;
                PRAGMA cache_size        = -32000;
            `, nil)
            return err
        },
    })
    // Mirror the sqlite3 query builder so PocketBase generates correct SQL
    dbx.BuilderFuncMap["pb_sqlite3"] = dbx.BuilderFuncMap["sqlite3"]
}

func main() {
    app := pocketbase.NewWithConfig(pocketbase.Config{
        DBConnect: func(dbPath string) (*dbx.DB, error) {
            return dbx.Open("pb_sqlite3", dbPath)
        },
    })
    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

Correct (ncruces/go-sqlite3 — no CGO, PRAGMAs via DSN query string):

package main

import (
    "log"

    "github.com/pocketbase/dbx"
    "github.com/pocketbase/pocketbase"
    _ "github.com/ncruces/go-sqlite3/driver"
    _ "github.com/ncruces/go-sqlite3/embed"
)

func main() {
    const pragmas = "?_pragma=busy_timeout(10000)" +
        "&_pragma=journal_mode(WAL)" +
        "&_pragma=journal_size_limit(200000000)" +
        "&_pragma=synchronous(NORMAL)" +
        "&_pragma=foreign_keys(ON)" +
        "&_pragma=temp_store(MEMORY)" +
        "&_pragma=cache_size(-32000)"

    app := pocketbase.NewWithConfig(pocketbase.Config{
        DBConnect: func(dbPath string) (*dbx.DB, error) {
            return dbx.Open("sqlite3", "file:"+dbPath+pragmas)
        },
    })
    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

Conditional custom driver with default fallback:

app := pocketbase.NewWithConfig(pocketbase.Config{
    DBConnect: func(dbPath string) (*dbx.DB, error) {
        // Use custom driver only for the main data file; fall back for auxiliary
        if strings.HasSuffix(dbPath, "data.db") {
            return dbx.Open("pb_sqlite3", dbPath)
        }
        return core.DefaultDBConnect(dbPath)
    },
})

Decision guide:

Need Driver
Default (no extensions) Built-in modernc.org/sqlite — no DBConnect config needed
FTS5, ICU, spatialite mattn/go-sqlite3 (CGO) or ncruces/go-sqlite3 (WASM, no CGO)
Reduce binary size go build -tags no_default_driver to exclude the default driver (~4 MB saved)
Conditional fallback Call core.DefaultDBConnect(dbPath) inside your DBConnect function

Reference: Extend with Go - Custom SQLite driver