
gosqlite
gosqlite is a SQLite driver for the Go programming language. It
is designed with the following goals in mind.
- Lightweight - Most methods should be little more than a small
wrapper around SQLite C functions.
- Performance - Where possible, methods should be available to
allow for the highest performance possible.
- Understandable - You should always know what SQLite functions
are being called and in what order.
- Unsurprising - Connections, PRAGMAs, transactions, bindings, and
stepping should work out of the box exactly as you would expect with
SQLite.
- Debuggable - When you encounter a SQLite error, the SQLite
documentation should be relevant and relatable to the Go code.
- Ergonomic - Where it makes sense, convenient compound methods
should exist to make tasks easy and to conform to Go standard
interfaces.
Most database drivers include a layer to work nicely with the Go
database/sql
interface, which introduces connection pooling and
behavior differences from pure SQLite. This driver does not include a
database/sql
interface.
Releases
Getting started
import "github.com/eatonphil/gosqlite"
Acquiring a connection
conn, err := gosqlite.Open("mydatabase.db")
if err != nil {
...
}
defer conn.Close()
conn.BusyTimeout(5 * time.Second)
Executing SQL
err = conn.Exec(`CREATE TABLE student(name TEXT, age INTEGER)`)
if err != nil {
...
}
err = conn.Exec(`INSERT INTO student VALUES (?, ?)`, "Bob", 18)
if err != nil {
...
}
Using Prepared Statements
stmt, err := conn.Prepare(`INSERT INTO student VALUES (?, ?)`)
if err != nil {
...
}
defer stmt.Close()
err = stmt.Bind("Bill", 18)
if err != nil {
...
}
hasRow, err := stmt.Step()
if err != nil {
...
}
err = stmt.Reset()
if err != nil {
...
}
Using Prepared Statements Conveniently
stmt, err := conn.Prepare(`INSERT INTO student VALUES (?, ?)`)
if err != nil {
...
}
defer stmt.Close()
err = stmt.Exec("John", 19)
if err != nil {
...
}
Using Queries Conveniently
stmt, err := conn.Prepare(`SELECT name, age FROM student WHERE age = ?`, 18)
if err != nil {
...
}
defer stmt.Close()
for {
hasRow, err := stmt.Step()
if err != nil {
...
}
if !hasRow {
break
}
var name string
var age int
err = stmt.Scan(&name, &age)
if err != nil {
...
}
fmt.Println("name:", name, "age:", age)
}
Getting columns that might be NULL
Scan can be convenient to use, but it doesn't handle NULL values. To
get full control of column values, there are column methods for each
type.
name, ok, err := stmt.ColumnText(0)
if err != nil {
...
}
if !ok {
}
age, ok, err := stmt.ColumnInt(1)
if err != nil {
...
}
if !ok {
}
ColumnBlob
returns a nil slice in the case of NULL.
blob, err := stmt.ColumnBlob(i)
if err != nil {
...
}
if blob == nil {
}
Using Transactions
err := conn.Begin()
if err != nil {
...
}
...
err = conn.Commit()
if err != nil {
...
}
Using Transactions Conveniently
With error handling in Go, it can be pretty inconvenient to ensure
that a transaction is rolled back in the case of an error. The
WithTx
method is provided, which accepts a function of work to do
inside of a transaction. WithTx
will begin the transaction and call
the function. If the function returns an error, the transaction will
be rolled back. If the function succeeds, the transaction will be
committed. WithTx
can be a little awkward to use, but it's
necessary. For example:
err := conn.WithTx(func() error {
return insertStudents(conn)
})
if err != nil {
...
}
func insertStudents(conn *gosqlite.Conn) error {
...
}
Advanced Features
- Binding parameters to statements using SQLite named parameters.
- SQLite Blob Incremental IO API.
- SQLite Online Backup API.
- SQLite Session extension.
- Supports setting a custom busy handler
- Supports callback hooks on commit, rollback, and update.
- Supports setting compile-Time authorization callbacks.
- If shared cache mode is enabled and one statement receives a
SQLITE_LOCKED
error, the SQLite
unlock_notify extension is
used to transparently block and try again when the conflicting
statement finishes.
- Compiled with SQLite support for JSON1, RTREE, FTS5, GEOPOLY, STAT4, and SOUNDEX.
- Compiled with SQLite support for OFFSET/LIMIT on UPDATE and DELETE statements.
- RawString and RawBytes can be used to reduce copying between Go and SQLite. Please use with caution.
Credit
This project began as a fork of https://github.com/bvinc/go-sqlite-lite.
FAQ
Why is there no database/sql
interface?
If a database/sql
interface is required, please use
https://github.com/mattn/go-gosqlite. Connection pooling causes
unnecessary overhead and weirdness. Transactions using Exec("BEGIN")
don't work as expected. Your connection does not correspond to
SQLite's concept of a connection. PRAGMA commands do not work as
expected. When you hit SQLite errors, such as locking or busy errors,
it's difficult to discover why since you don't know which connection
received which SQL and in what order.
What are the differences between this driver and the bvinc/go-sqlite-lite driver?
This driver was forked from bvinc/go-sqlite-lite
. It hadn't been
maintained in years and used an ancient version of SQLite.
Are finalizers provided to automatically close connections and statements?
No finalizers are used in this driver. You are responsible for
closing connections and statements. While I mostly agree with
finalizers for cleaning up most accidental resource leaks, in this
case, finalizers may fix errors such as locking errors while debugging
only to find that the code works unreliably in production. Removing
finalizers makes the behavior consistent.
Is it thread safe?
gosqlite
is as thread safe as SQLite SQLite with this driver
is compiled with -DSQLITE_THREADSAFE=2
which is Multi-thread
mode. In this mode, SQLite can be safely used by multiple threads
provided that no single database connection is used simultaneously in
two or more threads. This applies to goroutines. A single database
connection should not be used simultaneously between two goroutines.
It is safe to use separate connection instances concurrently, even if
they are accessing the same database file. For example:
c, _ := gosqlite.Open("sqlite.db")
go use(c)
go use(c)
c1, _ := gosqlite.Open("sqlite.db")
c2, _ := gosqlite.Open("sqlite.db")
go use(c1)
go use(c2)
Consult the SQLite documentation for more information.
https://www.sqlite.org/threadsafe.html
How do I pool connections for handling HTTP requests?
Opening new connections is cheap and connection pooling is generally
unnecessary for SQLite. I would recommend that you open a new
connection for each request that you're handling. This ensures that
each request is handled separately and the normal rules of SQLite
database/table locking apply.
If you've decided that pooling connections provides you with an
advantage, it would be outside the scope of this package and something
that you would need to implement and ensure works as needed.
License
This project is licensed under the BSD license.