pkg.go.dev

Package sqlite is a sql/database driver using a CGo-free port of the C SQLite3 library.

SQLite is an in-process implementation of a self-contained, serverless, zero-configuration, transactional SQL database engine.

Pluggable page cache

The package exposes a Go-facing wrapper for SQLite's SQLITE_CONFIG_PCACHE2 mechanism. Applications can supply their own page cache implementation by registering a PageCache before the first sql.Open via RegisterPageCache. See the docstrings on PageCache, Cache, and Page for the contract; the binding owns the sqlite3_pcache_page stub on behalf of the implementation and re-consults Cache.Fetch on every SQLite request, so a bounded and evicting purgeable cache works as the C contract intends.

Fragile modernc.org/libc dependency

When you import this package you should use in your go.mod file the exact same version of modernc.org/libc as seen in the go.mod file of this repository.

See the discussion at https://gitlab.com/cznic/sqlite/-/issues/177 for more details.

Thanks

This project is sponsored by Schleibinger Geräte Teubert u. Greim GmbH by allowing one of the maintainers to work on it also in office hours.

Supported platforms and architectures

These combinations of GOOS and GOARCH are currently supported

OS      Arch    SQLite version
------------------------------
darwin	amd64   3.53.3
darwin	arm64   3.53.3
freebsd	amd64   3.53.3
freebsd	arm64   3.53.3
linux	386     3.53.3
linux	amd64   3.53.3
linux	arm     3.53.3
linux	arm64   3.53.3
linux	loong64 3.53.3
linux	ppc64le 3.53.3
linux	riscv64 3.53.3
linux	s390x   3.53.3
openbsd	amd64   3.53.3
openbsd	arm64   3.53.3
windows	386     3.53.3
windows	amd64   3.53.3
windows	arm64   3.53.3

Benchmarks

The SQLite Drivers Benchmarks Game

Builders

Builder results available at:

https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fsqlite

Connecting to a database

To access a Sqlite database do something like

import (
	"database/sql"
	_ "modernc.org/sqlite"
)
...
db, err := sql.Open("sqlite", dsnURI)
...

NewConnector is an alternative entry point returning a driver.Connector for use with sql.OpenDB. It opens the same connections sql.Open does, from the same driver, and exists for callers that need to interpose on them -- tracing, metrics, or connection-scoped setup -- which sql.Open gives no access to. See its docstring for an example.

Debug and development versions

The transpiled SQLite sources under lib/, and the sqlite-vec sources under vec/, are not generated in this repository. They are produced by modernc.org/libsqlite3 and modernc.org/libsqlite_vec respectively, which own the transpilation and the SQLite compile-time options it uses, and are copied here by

$ make vendor

which reads them from checkouts of those two repositories placed next to this one. To build a debug or otherwise modified version, adjust the compile-time options in modernc.org/libsqlite3, regenerate there with 'make generate', and vendor the result here.

Hacking

This is an example of how to use the debug logs in modernc.org/libc when hunting a bug.

0:jnml@e5-1650:~/src/modernc.org/sqlite$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
0:jnml@e5-1650:~/src/modernc.org/sqlite$ git log -1
commit df33b8d15107f3cc777799c0fe105f74ef499e62 (HEAD -> master, tag: v1.21.1, origin/master, origin/HEAD, wips, ok)
Author: Jan Mercl <0xjnml@gmail.com>
Date:   Mon Mar 27 16:18:28 2023 +0200
    upgrade to SQLite 3.41.2
0:jnml@e5-1650:~/src/modernc.org/sqlite$ rm -f /tmp/libc.log ; go test -v -tags=libc.dmesg -run TestScalar ; ls -l /tmp/libc.log
test binary compiled for linux/amd64
=== RUN   TestScalar
--- PASS: TestScalar (0.09s)
PASS
ok  modernc.org/sqlite 0.128s
-rw-r--r-- 1 jnml jnml 76 Apr  6 11:22 /tmp/libc.log
0:jnml@e5-1650:~/src/modernc.org/sqlite$ cat /tmp/libc.log
[10723 sqlite.test] 2023-04-06 11:22:48.288066057 +0200 CEST m=+0.000707150
0:jnml@e5-1650:~/src/modernc.org/sqlite$

The /tmp/libc.log file is created as requested. No useful messages there because none are enabled in libc. Let's try to enable Xwrite as an example.

0:jnml@e5-1650:~/src/modernc.org/libc$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
modified:   libc_linux.go
no changes added to commit (use "git add" and/or "git commit -a")
0:jnml@e5-1650:~/src/modernc.org/libc$ git log -1
commit 1e22c18cf2de8aa86d5b19b165f354f99c70479c (HEAD -> master, tag: v1.22.3, origin/master, origin/HEAD)
Author: Jan Mercl <0xjnml@gmail.com>
Date:   Wed Feb 22 20:27:45 2023 +0100
    support sqlite 3.41 on linux targets
0:jnml@e5-1650:~/src/modernc.org/libc$ git diff
diff --git a/libc_linux.go b/libc_linux.go
index 1c2f482..ac1f08d 100644
--- a/libc_linux.go
+++ b/libc_linux.go
@@ -332,19 +332,19 @@ func Xwrite(t *TLS, fd int32, buf uintptr, count types.Size_t) types.Ssize_t {
                var n uintptr
                switch n, _, err = unix.Syscall(unix.SYS_WRITE, uintptr(fd), buf, uintptr(count)); err {
                case 0:
-                       // if dmesgs {
-                       //      // dmesg("%v: %d %#x: %#x\n%s", origin(1), fd, count, n, hex.Dump(GoBytes(buf, int(n))))
-                       //      dmesg("%v: %d %#x: %#x", origin(1), fd, count, n)
-                       // }
+                       if dmesgs {
+                               // dmesg("%v: %d %#x: %#x\n%s", origin(1), fd, count, n, hex.Dump(GoBytes(buf, int(n))))
+                               dmesg("%v: %d %#x: %#x", origin(1), fd, count, n)
+                       }
                        return types.Ssize_t(n)
                case errno.EAGAIN:
                        // nop
                }
        }
-       // if dmesgs {
-       //      dmesg("%v: fd %v, count %#x: %v", origin(1), fd, count, err)
-       // }
+       if dmesgs {
+               dmesg("%v: fd %v, count %#x: %v", origin(1), fd, count, err)
+       }
        t.setErrno(err)
        return -1
 }
0:jnml@e5-1650:~/src/modernc.org/libc$

We need to tell the Go build system to use our local, patched/debug libc. 'make work' sets up a go.work covering this and the sibling repositories; by hand it is:

0:jnml@e5-1650:~/src/modernc.org/sqlite$ go work use $(go env GOPATH)/src/modernc.org/libc
0:jnml@e5-1650:~/src/modernc.org/sqlite$ go work use .

And run the test again:

0:jnml@e5-1650:~/src/modernc.org/sqlite$ rm -f /tmp/libc.log ; go test -v -tags=libc.dmesg -run TestScalar ; ls -l /tmp/libc.log
test binary compiled for linux/amd64
=== RUN   TestScalar
--- PASS: TestScalar (0.26s)
PASS
ok   modernc.org/sqlite 0.285s
-rw-r--r-- 1 jnml jnml 918 Apr  6 11:29 /tmp/libc.log
0:jnml@e5-1650:~/src/modernc.org/sqlite$ cat /tmp/libc.log
[11910 sqlite.test] 2023-04-06 11:29:13.143589542 +0200 CEST m=+0.000689270
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x200: 0x200
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0xc: 0xc
[11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
[11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x200: 0x200
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x1000: 0x1000
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x1000: 0x1000
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0x4: 0x4
[11910 sqlite.test] libc_linux.go:337:Xwrite: 8 0xc: 0xc
[11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
[11910 sqlite.test] libc_linux.go:337:Xwrite: 7 0x1000: 0x1000
0:jnml@e5-1650:~/src/modernc.org/sqlite$

Sqlite documentation

See https://sqlite.org/docs.html

DBStatus* are the operations accepted by DBStatus.Status. They report their value differently depending on the op:

  • DBStatusLookasideUsed: current is the lookaside memory in use now; high is its high-water mark. The reset flag rebases the high-water mark to current. This is the only op that maintains a high-water mark.
  • Memory-usage ops (DBStatusCacheUsed, DBStatusSchemaUsed, DBStatusStmtUsed, DBStatusCacheUsedShared): current is the bytes in use now; high is always 0; the reset flag is ignored.
  • Running-counter ops (DBStatusCacheHit, DBStatusCacheMiss, DBStatusCacheWrite, DBStatusCacheSpill, DBStatusTempbufSpill): current is the cumulative count (bytes spilled, for DBStatusTempbufSpill); high is always 0. The reset flag zeroes current.
  • Lookaside event ops (DBStatusLookasideHit, DBStatusLookasideMissSize, DBStatusLookasideMissFull): the count is reported in high, not current (current is always 0). The reset flag zeroes high.
  • DBStatusDeferredFKs: current is 1 if the connection has unresolved deferred foreign-key constraints, else 0; high is always 0; the reset flag is ignored.

View Source

var ErrPageCacheConflict = errors.New(
	"sqlite: a different page cache module is already registered")

ErrPageCacheConflict is returned when a different PageCache has already been registered in this process. The same module value may be re-registered without error, which lets multiple library imports share a singleton without coordination.

View Source

var ErrPageCacheTooLate = errors.New(
	"sqlite: RegisterPageCache called after first Open; " +
		"SQLITE_CONFIG_PCACHE2 must be installed before any connection is opened")

ErrPageCacheTooLate is returned by RegisterPageCache when a SQLite connection has already been opened in this process. SQLITE_CONFIG_PCACHE2 must be installed before sqlite3_initialize, which is called implicitly by the first sqlite3_open_v2. After that point SQLite returns SQLITE_MISUSE and the engine cannot switch its page cache backend.

View Source

var (

	ErrorCodeString = map[int]string{
		sqlite3.SQLITE_ABORT:             "Callback routine requested an abort (SQLITE_ABORT)",
		sqlite3.SQLITE_AUTH:              "Authorization denied (SQLITE_AUTH)",
		sqlite3.SQLITE_BUSY:              "The database file is locked (SQLITE_BUSY)",
		sqlite3.SQLITE_CANTOPEN:          "Unable to open the database file (SQLITE_CANTOPEN)",
		sqlite3.SQLITE_CONSTRAINT:        "Abort due to constraint violation (SQLITE_CONSTRAINT)",
		sqlite3.SQLITE_CORRUPT:           "The database disk image is malformed (SQLITE_CORRUPT)",
		sqlite3.SQLITE_DONE:              "sqlite3_step() has finished executing (SQLITE_DONE)",
		sqlite3.SQLITE_EMPTY:             "Internal use only (SQLITE_EMPTY)",
		sqlite3.SQLITE_ERROR:             "Generic error (SQLITE_ERROR)",
		sqlite3.SQLITE_FORMAT:            "Not used (SQLITE_FORMAT)",
		sqlite3.SQLITE_FULL:              "Insertion failed because database is full (SQLITE_FULL)",
		sqlite3.SQLITE_INTERNAL:          "Internal logic error in SQLite (SQLITE_INTERNAL)",
		sqlite3.SQLITE_INTERRUPT:         "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)",
		sqlite3.SQLITE_IOERR | (1 << 8):  "(SQLITE_IOERR_READ)",
		sqlite3.SQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)",
		sqlite3.SQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)",
		sqlite3.SQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)",
		sqlite3.SQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)",
		sqlite3.SQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)",
		sqlite3.SQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)",
		sqlite3.SQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)",
		sqlite3.SQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)",
		sqlite3.SQLITE_IOERR | (2 << 8):  "(SQLITE_IOERR_SHORT_READ)",
		sqlite3.SQLITE_IOERR | (3 << 8):  "(SQLITE_IOERR_WRITE)",
		sqlite3.SQLITE_IOERR | (4 << 8):  "(SQLITE_IOERR_FSYNC)",
		sqlite3.SQLITE_IOERR | (5 << 8):  "(SQLITE_IOERR_DIR_FSYNC)",
		sqlite3.SQLITE_IOERR | (6 << 8):  "(SQLITE_IOERR_TRUNCATE)",
		sqlite3.SQLITE_IOERR | (7 << 8):  "(SQLITE_IOERR_FSTAT)",
		sqlite3.SQLITE_IOERR | (8 << 8):  "(SQLITE_IOERR_UNLOCK)",
		sqlite3.SQLITE_IOERR | (9 << 8):  "(SQLITE_IOERR_RDLOCK)",
		sqlite3.SQLITE_IOERR:             "Some kind of disk I/O error occurred (SQLITE_IOERR)",
		sqlite3.SQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)",
		sqlite3.SQLITE_LOCKED:            "A table in the database is locked (SQLITE_LOCKED)",
		sqlite3.SQLITE_MISMATCH:          "Data type mismatch (SQLITE_MISMATCH)",
		sqlite3.SQLITE_MISUSE:            "Library used incorrectly (SQLITE_MISUSE)",
		sqlite3.SQLITE_NOLFS:             "Uses OS features not supported on host (SQLITE_NOLFS)",
		sqlite3.SQLITE_NOMEM:             "A malloc() failed (SQLITE_NOMEM)",
		sqlite3.SQLITE_NOTADB:            "File opened that is not a database file (SQLITE_NOTADB)",
		sqlite3.SQLITE_NOTFOUND:          "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)",
		sqlite3.SQLITE_NOTICE:            "Notifications from sqlite3_log() (SQLITE_NOTICE)",
		sqlite3.SQLITE_PERM:              "Access permission denied (SQLITE_PERM)",
		sqlite3.SQLITE_PROTOCOL:          "Database lock protocol error (SQLITE_PROTOCOL)",
		sqlite3.SQLITE_RANGE:             "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)",
		sqlite3.SQLITE_READONLY:          "Attempt to write a readonly database (SQLITE_READONLY)",
		sqlite3.SQLITE_ROW:               "sqlite3_step() has another row ready (SQLITE_ROW)",
		sqlite3.SQLITE_SCHEMA:            "The database schema changed (SQLITE_SCHEMA)",
		sqlite3.SQLITE_TOOBIG:            "String or BLOB exceeds size limit (SQLITE_TOOBIG)",
		sqlite3.SQLITE_WARNING:           "Warnings from sqlite3_log() (SQLITE_WARNING)",
	}
)

Limit calls sqlite3_limit, see the docs at https://www.sqlite.org/c3ref/limit.html for details.

To get a sql.Conn from a *sql.DB, use (*sql.DB).Conn(). Limits are bound to the particular instance of 'c', so getting a new connection only to pass it to Limit is possibly not useful above querying what are the various configured default values.

func MustRegisterCollationUtf8(
	zName string,
	impl func(left, right string) int,
)

MustRegisterCollationUtf8 is like RegisterCollationUtf8 but panics on error.

MustRegisterDeterministicScalarFunction is like RegisterDeterministicScalarFunction but panics on error.

func MustRegisterFunction(
	zFuncName string,
	impl *FunctionImpl,
)

MustRegisterFunction is like RegisterFunction but panics on error.

func MustRegisterPageCache(m PageCache)

MustRegisterPageCache is like RegisterPageCache but panics on any error. Intended for init() use where a missing page cache is fatal. Mirrors the precedent set by MustRegisterDeterministicScalarFunction.

MustRegisterScalarFunction is like RegisterScalarFunction but panics on error.

NewConnector returns a driver.Connector that opens connections to dsn using the driver this package registers as "sqlite" -- the one carrying every function, collation, connection hook and virtual table module registered through RegisterFunction, RegisterScalarFunction, RegisterDeterministicScalarFunction, RegisterCollationUtf8, RegisterConnectionHook and vtab.RegisterModule. The dsn syntax and the supported query parameters are documented on Driver.Open.

The returned value is intended for sql.OpenDB:

c, err := sqlite.NewConnector("file:app.db?_pragma=foreign_keys(1)")
if err != nil {
	return err
}
db := sql.OpenDB(c)
defer db.Close()

For opening a database this is equivalent to sql.Open("sqlite", dsn). It exists for callers that need to interpose on the physical connections database/sql opens -- tracing, metrics, or connection-scoped setup. Such a caller can embed the returned Connector, override Connect, and pass its own wrapper to sql.OpenDB:

type tracer struct{ driver.Connector }
func (t tracer) Connect(ctx context.Context) (driver.Conn, error) {
	conn, err := t.Connector.Connect(ctx)
	// ... wrap conn ...
	return conn, err
}
base, err := sqlite.NewConnector(dsn)
if err != nil {
	return err
}
db := sql.OpenDB(tracer{base})

Reaching the same driver through sql.Open requires sql.Register, which is process-global, rejects a name it has already seen with a panic, and offers no way to undo a registration; a library doing the above would have to invent a unique name per configuration. sql.OpenDB registers nothing.

The dsn is checked here only as far as it can be without opening a database: a query string that does not parse, such as one carrying an invalid percent-escape, and conflicting vfs parameters are reported immediately. Everything else is validated when the connection is opened, so an unknown parameter or an out-of-range value is reported by Connect, and hence by the first use of the sql.DB, rather than by NewConnector.

The returned Connector is safe for concurrent use; database/sql calls Connect from multiple goroutines as it grows the pool. As with sql.Open, it does not itself open a connection.

RegisterCollationUtf8 makes a Go function available as a collation named zName. impl receives two UTF-8 strings: left and right. The result needs to be:

- 0 if left == right - 1 if left < right - +1 if left > right

impl must always return the same result given the same inputs. Additionally, it must have the following properties for all strings A, B and C: - if A==B, then B==A - if A==B and B==C, then A==C - if A<B, then B>A - if A<B and B<C, then A<C.

The new collation will be available to all new connections opened after executing RegisterCollationUtf8.

func RegisterConnectionHook(fn ConnectionHookFn)

RegisterConnectionHook registers a function to be called after each connection is opened. This is called after all the connection has been set up.

RegisterDeterministicScalarFunction registers a deterministic scalar function named zFuncName with nArg arguments. Passing -1 for nArg indicates the function is variadic. A deterministic function means that the function always gives the same output when the input parameters are the same.

The new function will be available to all new connections opened after executing RegisterDeterministicScalarFunction.

RegisterFunction registers a function named zFuncName with nArg arguments. Passing -1 for nArg indicates the function is variadic. The FunctionImpl determines whether the function is deterministic or not, and whether it is a scalar function (when Scalar is defined) or an aggregate function (when Scalar is not defined and MakeAggregate is defined).

The new function will be available to all new connections opened after executing RegisterFunction.

func RegisterPageCache(m PageCache) error

RegisterPageCache installs m as the process-global SQLite page cache via SQLITE_CONFIG_PCACHE2. It MUST be called before the first sql.Open or driver.Open in the program.

Concurrency contract:

  • Safe to call concurrently with itself and with other Register* entry points.
  • Blocks until any sql.Open calls currently in progress complete. Trade-off: a Register call may block for the duration of an in-flight Open. WAL recovery or cold-file-lock contention can make that wait visible.
  • Once any connection has been opened, returns ErrPageCacheTooLate without mutating the global module slot.
  • Calling twice with the same module value is a no-op success. Calling twice with a different value returns ErrPageCacheConflict.
  • A failed first install is sticky: every subsequent Register call returns the same error. Mutating the module fields after the first successful Register is silently ignored because SQLite has already copied the C methods table.

RegisterScalarFunction registers a scalar function named zFuncName with nArg arguments. Passing -1 for nArg indicates the function is variadic.

The new function will be available to all new connections opened after executing RegisterScalarFunction.

type Backup struct {
}

Backup object is used to manage progress and cleanup an online backup. It is returned by NewBackup or NewRestore.

Commit releases all resources associated with the Backup object but does not close the destination database connection.

The destination database connection is returned to the caller or an error if raised. It is the responsibility of the caller to handle the connection closure.

Finish releases all resources associated with the Backup object. The Backup object is invalid and may not be used following a call to Finish.

func (b *Backup) PageCount() int

PageCount returns the total number of pages in the source database at the conclusion of the most recent Backup.Step call. Pair with Backup.Remaining to compute progress as a fraction (PageCount - Remaining) / PageCount.

See https://www.sqlite.org/c3ref/backup_finish.html.

func (*Backup) Remaining added in v1.52.0

func (b *Backup) Remaining() int

Remaining returns the number of source-database pages still to be backed up at the conclusion of the most recent Backup.Step call. The value is useful for driving progress UIs that need to estimate how much work is left.

If Step has not yet been called on this Backup, or if the most recent Step returned false (SQLITE_DONE), Remaining returns 0.

See https://www.sqlite.org/c3ref/backup_finish.html.

Step will copy up to n pages between the source and destination databases specified by the backup object. If n is negative, all remaining source pages are copied. If it successfully copies n pages and there are still more pages to be copied, then the function returns true with no error. If it successfully finishes copying all pages from source to destination, then it returns false with no error. If an error occurs while running, then an error is returned.

type Cache interface {


	SetSize(n int)


	PageCount() int


	Fetch(key uint32, mode FetchMode) Page


	Unpin(p Page, discard bool)


	Rekey(p Page, oldKey, newKey uint32)


	Truncate(limit uint32)


	Destroy()


	Shrink()
}

Cache is one database's worth of cached pages. All callbacks for a single Cache are serialised by the SQLite engine: this driver opens every connection SQLITE_OPEN_FULLMUTEX without shared-cache mode, and database/sql never invokes one driver.Conn from two goroutines, so an implementation does not need to synchronise per-Cache state against concurrent calls.

Implementations should NOT call RegisterPageCache directly or transitively. Callbacks run under the openGate read lock that the Open path holds, and a re-entrant Register would deadlock on the gate's write lock.

type CommitHookFn func() int32

ConnectionHookFn function type for a connection hook on the Driver. Connection hooks are called after the connection has been set up.

type DBStatus interface {


	Status(op DBStatusOp, reset bool) (current, high int, err error)
}

DBStatus exposes sqlite3_db_status, the per-connection runtime counters (cache hit/miss/write/spill rates, schema and prepared-statement memory, lookaside usage, deferred foreign keys). Reach it through the database/sql escape hatch, the same way as FileControl:

err := sqlConn.Raw(func(dc any) error {
	cur, _, err := dc.(sqlite.DBStatus).Status(sqlite.DBStatusCacheSpill, false)
	if err != nil {
		return err
	}
	// use cur
	return nil
})

DBStatusOp identifies a per-connection runtime counter readable through DBStatus.Status. The values mirror the SQLITE_DBSTATUS_* verbs of the C API; the distinct type keeps a counter from a different family (for example a file-control or db-config op) from compiling in its place.

See https://www.sqlite.org/c3ref/c_dbstatus_options.html for the per-op semantics.

type Driver struct {
}

Driver implements database/sql/driver.Driver.

Registration functions and methods must be called before the first call to Open.

Most code has no use for this type. sql.Open("sqlite", dsn) and NewConnector both go through the driver this package registers as "sqlite", which carries everything registered with RegisterFunction, RegisterScalarFunction, RegisterDeterministicScalarFunction, RegisterCollationUtf8, RegisterConnectionHook and vtab.RegisterModule.

A Driver a caller constructs is not equivalent to that one. Its fields are unexported, so it starts out with no functions, collations or connection hooks, and the only way to give it any is Driver.RegisterConnectionHook; the package-level registration functions always apply to the registered driver, never to a constructed one. Connections it opens therefore run without the package-level functions and collations -- and where such a registration overrides a SQLite built-in of the same name, they run with SQLite's built-in in force instead. Virtual table modules are the one exception: they are held process-globally and reach every Driver.

Constructing one is supported for the private-hook pattern: a driver registered under a name of its own with sql.Register, so that its connection hooks apply to its own connections rather than to every connection in the process. Prefer sql.Open or NewConnector for anything else.

Open returns a new connection to the database. The name is a string in a driver-specific format.

Open may return a cached connection (one previously closed), but doing so is unnecessary; the sql package maintains a pool of idle connections for efficient re-use.

The returned connection is only used by one goroutine at a time.

The name may be a filename, e.g., "/tmp/mydata.sqlite", or a URI, in which case it may include a '?' followed by one or more query parameters. For example, "file:///tmp/mydata.sqlite?_pragma=foreign_keys(1)&_time_format=sqlite". The supported query parameters are:

_pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA keyword added for you). May be specified more than once, '&'-separated. For more information on supported PRAGMAs see: https://www.sqlite.org/pragma.html

The following shorthand keys set common PRAGMAs for easier DSN compatibility when migrating from github.com/mattn/go-sqlite3. Each value is validated against the same set github.com/mattn/go-sqlite3 accepts (case-insensitive); an unrecognized value fails the connection with an error instead of being silently ignored. The keys are applied in a fixed order, independent of the order they appear in the DSN: _busy_timeout and _auto_vacuum first (auto_vacuum must be set before the database is first written), then the _pragma values, then the remaining keys, and _query_only last. Where a shorthand key and a _pragma set the same PRAGMA, whichever is applied later in that order wins. If a key and its alias are both supplied, the alias (the second name below) wins, matching github.com/mattn/go-sqlite3; supplying the alias with an empty value therefore suppresses the PRAGMA rather than deferring to the primary key. Accepted values:

_busy_timeout, _timeout   -> PRAGMA busy_timeout   (an integer)
_foreign_keys, _fk        -> PRAGMA foreign_keys   (0 1 false true no yes off on)
_journal_mode, _journal   -> PRAGMA journal_mode   (DELETE TRUNCATE PERSIST MEMORY WAL OFF)
_synchronous, _sync       -> PRAGMA synchronous    (0 OFF 1 NORMAL 2 FULL 3 EXTRA)
_auto_vacuum, _vacuum     -> PRAGMA auto_vacuum    (0 NONE 1 FULL 2 INCREMENTAL)
_query_only               -> PRAGMA query_only     (0 1 false true no yes off on)

All DSN parameters that can be validated are validated before any of them is applied, so a DSN carrying a typo fails without having executed the PRAGMAs that precede it -- a rejected DSN does not leave the database converted to WAL or with auto_vacuum already set.

Unlike these validated shorthand keys, each _pragma value is executed verbatim (with PRAGMA prepended) and is not validated, so a DSN that includes _pragma must come from a trusted source. It is also the one case that can still fail partway: a bad _pragma is only rejected by SQLite as it runs, after any earlier _pragma in the list has taken effect.

_time_format: The name of a format to use when writing time values to the database. The currently supported values are (1) "sqlite" for YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM (format 4 from https://www.sqlite.org/lang_datefunc.html#time_values with sub-second precision and timezone specifier) and (2) "datetime" for YYYY-MM-DD HH:MM:SS (format 3, matching the output of SQLite's datetime() function). If this parameter is not specified, then the default String() format will be used.

_time_integer_format: The name of a integer format to use when writing time values. By default, the time is stored as string and the format can be set with _time_format parameter. If _time_integer_format is set, the time will be stored as an integer and the integer value will depend on the integer format. If you decide to set both _time_format and _time_integer_format, the time will be converted as integer and the _time_format value will be ignored. Currently the supported value are "unix","unix_milli", "unix_micro" and "unix_nano", which corresponds to seconds, milliseconds, microseconds or nanoseconds since unixepoch (1 January 1970 00:00:00 UTC).

_inttotime: Enable conversion of time column (DATE, DATETIME,TIMESTAMP) from integer to time if the field contain integer (int64).

_texttotime: Enable ColumnTypeScanType to report time.Time instead of string for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP. It also best-effort upgrades date-shaped TEXT values from columns SQLite reports with an empty declared type (aggregates and expressions such as MAX(d) or upper(d), subqueries, and typeless real columns) to time.Time, since the declared-type test cannot catch those (#248). When that upgrade fires, a Scan into interface{} yields a time.Time where it previously yielded a string, and a Scan into *string receives the value reformatted to RFC3339Nano rather than the raw stored text. A value that does not parse as a time is delivered unchanged as the original string.

_timezone: A timezone to use for all time reads and writes, such as "UTC". The value is parsed by time.LoadLocation. Writes will convert to the timezone before formatting as a string; it does not impact _inttotime integer values, as they always use UTC. Reads will interpret timezone-less strings as being in this timezone. Values that are in a known timezone, such as a string with a timezone specifier or an integer with _inttotime (specified to be in UTC), will be converted to this timezone.

_txlock: The locking behavior to use when beginning a transaction. May be "deferred" (the default), "immediate", or "exclusive" (case insensitive). See: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions

_dqs: Opt-in toggle for SQLite's double-quoted string literal compatibility quirk on the connection. Accepts the values strconv.ParseBool understands ("0"/"1", "false"/"true", "f"/"t", case-insensitive). When absent or set to a true value, SQLite's built-in behavior is unchanged: a double-quoted identifier that fails to resolve is silently re-interpreted as a string literal. When set to a false value, SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML are both turned off via sqlite3_db_config so that mistakes hidden by the legacy fallback surface as a parse error instead. See: https://www.sqlite.org/quirks.html#dblquote and https://gitlab.com/cznic/sqlite/-/issues/61

_error_rc: Opt-in error-string reporting mode for synthesised errors. Accepts the values strconv.ParseBool understands ("0"/"1", "false"/"true", "f"/"t", case-insensitive). When absent or set to a false value, the legacy "errstr: errmsg (rc)" form is preserved byte-for-byte: the canonical sqlite3_errstr(rc) and the connection's sqlite3_errmsg(db) are concatenated even when the latter belongs to a different operation, which can read as misleading on open-time failures such as SQLITE_CANTOPEN reporting "out of memory". When set to a true value, the appended errmsg is suppressed if sqlite3_extended_errcode(db) is inconsistent with the operation rc (full match first, primary code as fallback); in that case the canonical errstr(rc) is used alone. The Code() returned by the driver's *Error is unchanged in either mode. The parameter is parsed before sqlite3_open_v2 so open-time errors are covered. See https://gitlab.com/cznic/sqlite/-/issues/230.

vfs: The name of the SQLite VFS to open the database with. Note the absent underscore prefix: this is the same parameter SQLite recognizes in a file: URI, and its value is passed on as the sqlite3_open_v2 zVfs argument. It selects any VFS registered with SQLite, in particular one returned by modernc.org/sqlite/vfs.New, which exposes a Go fs.FS as a read-only VFS. When absent or empty the default VFS is used. Supplying the parameter more than once with values that differ is an error.

func (d *Driver) RegisterConnectionHook(fn ConnectionHookFn)

RegisterConnectionHook registers a function to be called after each connection is opened. This is called after all the connection has been set up.

The hook applies only to connections opened by d. To register one on the driver this package registers as "sqlite", and so on the connections sql.Open and NewConnector hand out, use the package-level RegisterConnectionHook.

type Error struct {
}

Error represents sqlite library error code.

func (e *Error) Code() int

Code returns the sqlite result code for this error.

Error implements error.

FetchMode tells Cache.Fetch how aggressively to allocate when the requested key is absent. It matches the createFlag of SQLite's xFetch (https://sqlite.org/c3ref/pcache_methods2.html).

const (


	FetchLookup FetchMode = 0


	FetchCreateEasy FetchMode = 1


	FetchCreateForce FetchMode = 2
)

Access to sqlite3_file_control

type FunctionContext struct {
}

FunctionContext represents the context user defined functions execute in. Fields and/or methods of this type may get addedd in the future.

FunctionImpl describes an application-defined SQL function. If Scalar is set, it is treated as a scalar function; otherwise, it is treated as an aggregate function using MakeAggregate.

type HookRegisterer interface {
	RegisterPreUpdateHook(PreUpdateHookFn)
	RegisterCommitHook(CommitHookFn)
	RegisterRollbackHook(RollbackHookFn)
}

Page is one cache entry. Buf and Extra return pointers into implementation-owned memory that MUST remain valid and at the same addresses for the duration of the pin: from the Fetch that returned the Page until the matching Unpin. While the page is unpinned the implementation is free to release the memory; the next Fetch for the same key will be consulted afresh and may return either the same Page (memory retained) or a different one.

The memory MUST be off-heap: libc.Xmalloc, sqlite3_malloc, mmap, or an equivalent allocator outside the Go heap. Go-heap memory is forbidden, including memory pinned with runtime.Pinner: SQLite stores Extra addresses inside its own C structures and performs interior pointer arithmetic on them (it overlays PgHdr at the head of Extra), which trips Go's checkptr enforcement under -race the moment _sqlite3PcacheFetchFinish runs. Pinned slices preserve the allocation but lose checkptr provenance through the binding's unsafe.Pointer round-trip, so the failure surfaces only under the race detector and not in normal test runs.

Page values are compared by the binding to detect whether the implementation retained or replaced the cached entry across a Fetch cycle, so Page MUST be a comparable type. Pointer-backed implementations (the natural shape) satisfy this automatically.

Buf must be at least pageSize bytes and is where SQLite stores the database page contents. Extra must be at least extraSize bytes (the extraSize passed to PageCache.Create, which already includes SQLite's PgHdr overhead) and is treated by SQLite as opaque scratch space. Implementations should zero Extra on a freshly-allocated Page so SQLite's PgHdr backpointer is read as null; the binding does not touch Extra contents.

type PageCache interface {
	Create(pageSize, extraSize int, purgeable bool) (Cache, error)
}

PageCache is the factory for per-database Cache instances. SQLite calls Create once per open database; each call must return a fresh Cache with the given pageSize and extraSize. The extraSize includes SQLite's private PgHdr overhead and must be honoured as the opaque size of every Page's Extra buffer.

purgeable is advisory: when false (in-memory databases), SQLite will only call Unpin with discard=true and the cache is permitted to free every page on Unpin. When true, the cache may retain unpinned pages for re-use.

type PreUpdateHookFn func(SQLitePreUpdateData)
type RollbackHookFn func()

Count returns the number of columns in the row

Depth returns the source path of the write, see sqlite3_preupdate_depth()

New populates dest with the replacement row data. This works similar to database/sql's Rows.Scan()

Old populates dest with the row data to be replaced. This works similar to database/sql's Rows.Scan()

Read the original on pkg.go.dev ↗