Files
sqlite3/driver/driver.go

422 lines
8.8 KiB
Go
Raw Normal View History

2023-02-14 18:21:18 +00:00
// Package driver provides a database/sql driver for SQLite.
2023-02-28 14:50:15 +00:00
//
// Importing package driver registers a [database/sql] driver named "sqlite3".
// You may also need to import package embed.
//
// import _ "github.com/ncruces/go-sqlite3/driver"
// import _ "github.com/ncruces/go-sqlite3/embed"
//
// The data source name for "sqlite3" databases can be a filename or a "file:" [URI].
//
// The [TRANSACTION] mode can be specified using "_txlock":
//
// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
//
// [PRAGMA] statements can be specified using "_pragma":
//
// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)")
//
2023-05-19 13:47:12 +01:00
// If no PRAGMAs are specified, a busy timeout of 1 minute
2023-02-28 14:50:15 +00:00
// and normal locking mode are used.
//
2023-05-25 11:14:18 +01:00
// Order matters:
// busy timeout and locking mode should be the first PRAGMAs set, in that order.
//
2023-02-28 14:50:15 +00:00
// [URI]: https://www.sqlite.org/uri.html
// [PRAGMA]: https://www.sqlite.org/pragma.html
// [TRANSACTION]: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
2023-02-14 18:21:18 +00:00
package driver
import (
2023-02-18 02:16:11 +00:00
"context"
2023-02-14 18:21:18 +00:00
"database/sql"
"database/sql/driver"
2023-02-20 13:30:01 +00:00
"fmt"
2023-02-17 02:21:07 +00:00
"io"
2023-02-18 12:20:42 +00:00
"net/url"
"strings"
2023-02-17 02:21:07 +00:00
"time"
2023-02-14 18:21:18 +00:00
"github.com/ncruces/go-sqlite3"
2023-03-29 15:01:25 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
2023-02-14 18:21:18 +00:00
)
func init() {
sql.Register("sqlite3", sqlite{})
}
type sqlite struct{}
2023-02-28 16:03:31 +00:00
func (sqlite) Open(name string) (_ driver.Conn, err error) {
2023-04-17 00:29:20 +01:00
var c conn
2023-05-30 13:39:34 +01:00
c.Conn, err = sqlite3.Open(name)
2023-02-17 16:19:55 +00:00
if err != nil {
return nil, err
}
2023-02-19 16:16:13 +00:00
2023-06-06 03:47:02 +01:00
c.txBegin = "BEGIN"
2023-02-28 16:03:31 +00:00
var pragmas []string
if strings.HasPrefix(name, "file:") {
if _, after, ok := strings.Cut(name, "?"); ok {
query, _ := url.ParseQuery(after)
switch s := query.Get("_txlock"); s {
case "":
2023-04-17 00:29:20 +01:00
c.txBegin = "BEGIN"
2023-02-28 16:03:31 +00:00
case "deferred", "immediate", "exclusive":
2023-04-17 00:29:20 +01:00
c.txBegin = "BEGIN " + s
2023-02-28 16:03:31 +00:00
default:
c.Close()
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", s)
}
pragmas = query["_pragma"]
2023-02-19 16:16:13 +00:00
}
2023-02-18 12:20:42 +00:00
}
2023-02-28 16:03:31 +00:00
if len(pragmas) == 0 {
2023-05-30 13:39:34 +01:00
err := c.Conn.Exec(`
2023-04-17 00:29:20 +01:00
PRAGMA busy_timeout=60000;
2023-05-25 11:14:18 +01:00
PRAGMA locking_mode=normal;
2023-04-17 00:29:20 +01:00
`)
if err != nil {
c.Close()
return nil, err
}
c.reusable = true
} else {
2023-05-30 13:39:34 +01:00
s, _, err := c.Conn.Prepare(`
2023-04-17 00:29:20 +01:00
SELECT * FROM
PRAGMA_locking_mode,
PRAGMA_query_only;
2023-02-28 16:03:31 +00:00
`)
if err != nil {
c.Close()
return nil, err
}
2023-04-17 00:29:20 +01:00
if s.Step() {
c.reusable = s.ColumnText(0) == "normal"
c.readOnly = s.ColumnRawText(1)[0] // 0 or 1
}
err = s.Close()
if err != nil {
c.Close()
return nil, err
}
2023-02-17 16:19:55 +00:00
}
2023-05-30 13:39:34 +01:00
return &c, nil
2023-02-14 18:21:18 +00:00
}
2023-02-18 12:20:42 +00:00
type conn struct {
2023-05-30 13:39:34 +01:00
*sqlite3.Conn
2023-03-08 18:05:18 +00:00
txBegin string
txCommit string
txRollback string
2023-04-17 00:29:20 +01:00
reusable bool
readOnly byte
2023-02-18 12:20:42 +00:00
}
2023-02-17 02:21:07 +00:00
var (
2023-02-17 16:19:55 +00:00
// Ensure these interfaces are implemented:
2023-05-30 13:39:34 +01:00
_ driver.ExecerContext = &conn{}
_ driver.ConnBeginTx = &conn{}
_ driver.Validator = &conn{}
_ sqlite3.DriverConn = &conn{}
2023-02-17 02:21:07 +00:00
)
2023-05-30 13:39:34 +01:00
func (c *conn) IsValid() bool {
2023-04-17 00:29:20 +01:00
return c.reusable
2023-03-10 15:50:11 +00:00
}
2023-05-30 13:39:34 +01:00
func (c *conn) Begin() (driver.Tx, error) {
2023-02-19 16:16:13 +00:00
return c.BeginTx(context.Background(), driver.TxOptions{})
}
2023-05-30 13:39:34 +01:00
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
2023-02-19 16:16:13 +00:00
txBegin := c.txBegin
2023-02-25 15:34:24 +00:00
c.txCommit = `COMMIT`
2023-03-08 18:05:18 +00:00
c.txRollback = `ROLLBACK`
2023-02-19 16:16:13 +00:00
if opts.ReadOnly {
txBegin = `
2023-02-20 13:30:01 +00:00
BEGIN deferred;
2023-02-25 15:34:24 +00:00
PRAGMA query_only=on`
2023-03-08 18:05:18 +00:00
c.txCommit = `
ROLLBACK;
2023-04-17 00:29:20 +01:00
PRAGMA query_only=` + string(c.readOnly)
2023-03-08 18:05:18 +00:00
c.txRollback = c.txCommit
}
switch opts.Isolation {
default:
2023-03-29 15:01:25 +01:00
return nil, util.IsolationErr
2023-03-08 18:05:18 +00:00
case
driver.IsolationLevel(sql.LevelDefault),
driver.IsolationLevel(sql.LevelSerializable):
break
2023-02-19 16:16:13 +00:00
}
2023-05-30 13:39:34 +01:00
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
err := c.Conn.Exec(txBegin)
2023-02-17 02:21:07 +00:00
if err != nil {
return nil, err
}
return c, nil
}
2023-05-30 13:39:34 +01:00
func (c *conn) Commit() error {
err := c.Conn.Exec(c.txCommit)
if err != nil && !c.GetAutocommit() {
2023-02-17 02:21:07 +00:00
c.Rollback()
}
return err
}
2023-02-14 18:21:18 +00:00
2023-05-30 13:39:34 +01:00
func (c *conn) Rollback() error {
return c.Conn.Exec(c.txRollback)
2023-02-17 02:21:07 +00:00
}
2023-02-14 18:21:18 +00:00
2023-05-30 13:39:34 +01:00
func (c *conn) Prepare(query string) (driver.Stmt, error) {
return c.PrepareContext(context.Background(), query)
}
func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
s, tail, err := c.Conn.Prepare(query)
2023-02-17 02:21:07 +00:00
if err != nil {
return nil, err
}
2023-02-18 02:57:47 +00:00
if tail != "" {
// Check if the tail contains any SQL.
2023-05-30 13:39:34 +01:00
st, _, err := c.Conn.Prepare(tail)
2023-02-18 02:57:47 +00:00
if err != nil {
s.Close()
2023-02-18 02:57:47 +00:00
return nil, err
}
if st != nil {
2023-02-18 02:57:47 +00:00
s.Close()
st.Close()
2023-03-29 15:01:25 +01:00
return nil, util.TailErr
2023-02-18 02:57:47 +00:00
}
}
2023-05-30 13:39:34 +01:00
return &stmt{s, c.Conn}, nil
2023-02-17 02:21:07 +00:00
}
2023-05-30 13:39:34 +01:00
func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
2023-02-18 02:57:47 +00:00
if len(args) != 0 {
// Slow path.
return nil, driver.ErrSkip
}
2023-05-30 13:39:34 +01:00
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
2023-02-18 02:57:47 +00:00
2023-05-30 13:39:34 +01:00
err := c.Conn.Exec(query)
2023-02-18 02:57:47 +00:00
if err != nil {
return nil, err
}
2023-05-30 13:39:34 +01:00
return newResult(c.Conn), nil
2023-05-17 14:29:59 +01:00
}
2023-02-17 02:21:07 +00:00
type stmt struct {
2023-05-30 13:39:34 +01:00
Stmt *sqlite3.Stmt
Conn *sqlite3.Conn
2023-02-17 02:21:07 +00:00
}
2023-02-17 16:19:55 +00:00
var (
// Ensure these interfaces are implemented:
2023-05-30 13:39:34 +01:00
_ driver.StmtExecContext = &stmt{}
_ driver.StmtQueryContext = &stmt{}
_ driver.NamedValueChecker = &stmt{}
2023-02-17 16:19:55 +00:00
)
2023-05-30 13:39:34 +01:00
func (s *stmt) Close() error {
return s.Stmt.Close()
2023-02-17 02:21:07 +00:00
}
2023-05-30 13:39:34 +01:00
func (s *stmt) NumInput() int {
n := s.Stmt.BindCount()
2023-02-20 13:30:01 +00:00
for i := 1; i <= n; i++ {
2023-05-30 13:39:34 +01:00
if s.Stmt.BindName(i) != "" {
2023-02-20 13:30:01 +00:00
return -1
}
}
return n
2023-02-17 02:21:07 +00:00
}
2023-02-18 02:16:11 +00:00
// Deprecated: use ExecContext instead.
2023-05-30 13:39:34 +01:00
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
2023-02-18 02:16:11 +00:00
return s.ExecContext(context.Background(), namedValues(args))
}
// Deprecated: use QueryContext instead.
2023-05-30 13:39:34 +01:00
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
2023-02-18 02:16:11 +00:00
return s.QueryContext(context.Background(), namedValues(args))
}
2023-05-30 13:39:34 +01:00
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
2023-02-20 13:38:03 +00:00
// Use QueryContext to setup bindings.
// No need to close rows: that simply resets the statement, exec does the same.
2023-02-18 02:16:11 +00:00
_, err := s.QueryContext(ctx, args)
2023-02-14 18:21:18 +00:00
if err != nil {
return nil, err
}
2023-02-17 02:21:07 +00:00
2023-05-30 13:39:34 +01:00
err = s.Stmt.Exec()
2023-02-17 02:21:07 +00:00
if err != nil {
return nil, err
}
2023-05-30 13:39:34 +01:00
return newResult(s.Conn), nil
2023-02-17 02:21:07 +00:00
}
2023-05-30 13:39:34 +01:00
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
err := s.Stmt.ClearBindings()
2023-02-18 02:16:11 +00:00
if err != nil {
return nil, err
}
var ids [3]int
for _, arg := range args {
ids := ids[:0]
if arg.Name == "" {
ids = append(ids, arg.Ordinal)
} else {
for _, prefix := range []string{":", "@", "$"} {
2023-05-30 13:39:34 +01:00
if id := s.Stmt.BindIndex(prefix + arg.Name); id != 0 {
2023-02-18 02:16:11 +00:00
ids = append(ids, id)
}
}
}
for _, id := range ids {
switch a := arg.Value.(type) {
case bool:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindBool(id, a)
2023-02-22 14:19:56 +00:00
case int:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindInt(id, a)
2023-02-18 02:16:11 +00:00
case int64:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindInt64(id, a)
2023-02-18 02:16:11 +00:00
case float64:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindFloat(id, a)
2023-02-18 02:16:11 +00:00
case string:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindText(id, a)
2023-02-18 02:16:11 +00:00
case []byte:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindBlob(id, a)
2023-02-22 14:19:56 +00:00
case sqlite3.ZeroBlob:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindZeroBlob(id, int64(a))
2023-02-18 02:16:11 +00:00
case time.Time:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindTime(id, a, sqlite3.TimeFormatDefault)
2023-02-18 02:16:11 +00:00
case nil:
2023-05-30 13:39:34 +01:00
err = s.Stmt.BindNull(id)
2023-02-18 02:16:11 +00:00
default:
2023-03-29 15:01:25 +01:00
panic(util.AssertErr())
2023-02-18 02:16:11 +00:00
}
2023-02-17 02:21:07 +00:00
}
if err != nil {
return nil, err
}
}
2023-02-18 02:16:11 +00:00
2023-05-30 13:39:34 +01:00
return &rows{ctx, s.Stmt, s.Conn}, nil
2023-02-17 02:21:07 +00:00
}
2023-05-30 13:39:34 +01:00
func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
2023-02-22 14:19:56 +00:00
switch arg.Value.(type) {
case bool, int, int64, float64, string, []byte,
sqlite3.ZeroBlob, time.Time, nil:
return nil
default:
return driver.ErrSkip
}
}
2023-05-08 10:34:33 +01:00
func newResult(c *sqlite3.Conn) driver.Result {
rows := c.Changes()
if rows != 0 {
id := c.LastInsertRowID()
if id != 0 {
return result{id, rows}
}
}
return resultRowsAffected(rows)
}
2023-02-17 02:21:07 +00:00
type result struct{ lastInsertId, rowsAffected int64 }
func (r result) LastInsertId() (int64, error) {
return r.lastInsertId, nil
}
func (r result) RowsAffected() (int64, error) {
return r.rowsAffected, nil
}
2023-05-08 10:34:33 +01:00
type resultRowsAffected int64
func (r resultRowsAffected) LastInsertId() (int64, error) {
return 0, nil
}
func (r resultRowsAffected) RowsAffected() (int64, error) {
return int64(r), nil
}
2023-02-18 02:16:11 +00:00
type rows struct {
ctx context.Context
2023-05-30 13:39:34 +01:00
Stmt *sqlite3.Stmt
Conn *sqlite3.Conn
2023-02-18 02:16:11 +00:00
}
2023-02-17 02:21:07 +00:00
2023-05-30 13:39:34 +01:00
func (r *rows) Close() error {
return r.Stmt.Reset()
2023-02-17 02:21:07 +00:00
}
2023-05-30 13:39:34 +01:00
func (r *rows) Columns() []string {
count := r.Stmt.ColumnCount()
2023-02-17 02:21:07 +00:00
columns := make([]string, count)
for i := range columns {
2023-05-30 13:39:34 +01:00
columns[i] = r.Stmt.ColumnName(i)
2023-02-17 02:21:07 +00:00
}
return columns
2023-02-14 18:21:18 +00:00
}
2023-05-30 13:39:34 +01:00
func (r *rows) Next(dest []driver.Value) error {
old := r.Conn.SetInterrupt(r.ctx)
defer r.Conn.SetInterrupt(old)
2023-02-18 02:16:11 +00:00
2023-05-30 13:39:34 +01:00
if !r.Stmt.Step() {
if err := r.Stmt.Err(); err != nil {
2023-02-17 12:30:07 +00:00
return err
2023-02-17 02:21:07 +00:00
}
2023-02-17 12:30:07 +00:00
return io.EOF
2023-02-17 02:21:07 +00:00
}
2023-02-14 18:21:18 +00:00
2023-02-17 02:21:07 +00:00
for i := range dest {
2023-05-30 13:39:34 +01:00
switch r.Stmt.ColumnType(i) {
2023-02-17 02:21:07 +00:00
case sqlite3.INTEGER:
2023-05-30 13:39:34 +01:00
dest[i] = r.Stmt.ColumnInt64(i)
2023-02-17 02:21:07 +00:00
case sqlite3.FLOAT:
2023-05-30 13:39:34 +01:00
dest[i] = r.Stmt.ColumnFloat(i)
2023-02-17 02:21:07 +00:00
case sqlite3.BLOB:
2023-05-30 13:39:34 +01:00
dest[i] = r.Stmt.ColumnRawBlob(i)
2023-04-18 01:00:59 +01:00
case sqlite3.TEXT:
2023-05-30 13:39:34 +01:00
dest[i] = stringOrTime(r.Stmt.ColumnRawText(i))
2023-02-17 10:40:43 +00:00
case sqlite3.NULL:
2023-02-17 12:30:07 +00:00
if buf, ok := dest[i].([]byte); ok {
dest[i] = buf[0:0]
} else {
dest[i] = nil
}
2023-02-17 10:40:43 +00:00
default:
2023-03-29 15:01:25 +01:00
panic(util.AssertErr())
2023-02-17 02:21:07 +00:00
}
}
2023-02-14 18:21:18 +00:00
2023-05-30 13:39:34 +01:00
return r.Stmt.Err()
2023-02-17 02:21:07 +00:00
}