blob: 982e08ec04f6dc97d34c20073b0a742fd771fb70 [file] [log] [blame]
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sqlite3 provides access to the SQLite library, version 3.
//
// The package has no exported API.
// It registers a driver for the standard Go database/sql package.
//
// import _ "code.google.com/p/gosqlite/sqlite3"
//
// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
package sqlite
/*
#cgo LDFLAGS: -lsqlite3
#include <sqlite3.h>
#include <stdlib.h>
// These wrappers are necessary because SQLITE_TRANSIENT
// is a pointer constant, and cgo doesn't translate them correctly.
// The definition in sqlite3.h is:
//
// typedef void (*sqlite3_destructor_type)(void*);
// #define SQLITE_STATIC ((sqlite3_destructor_type)0)
// #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
}
static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
}
*/
import "C"
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"io"
"strings"
"time"
"unsafe"
)
func init() {
sql.Register("sqlite3", impl{})
}
type errno int
func (e errno) Error() string {
s := errText[e]
if s == "" {
return fmt.Sprintf("errno %d", int(e))
}
return s
}
var (
errError error = errno(1) // /* SQL error or missing database */
errInternal error = errno(2) // /* Internal logic error in SQLite */
errPerm error = errno(3) // /* Access permission denied */
errAbort error = errno(4) // /* Callback routine requested an abort */
errBusy error = errno(5) // /* The database file is locked */
errLocked error = errno(6) // /* A table in the database is locked */
errNoMem error = errno(7) // /* A malloc() failed */
errReadOnly error = errno(8) // /* Attempt to write a readonly database */
errInterrupt error = errno(9) // /* Operation terminated by sqlite3_interrupt()*/
errIOErr error = errno(10) // /* Some kind of disk I/O error occurred */
errCorrupt error = errno(11) // /* The database disk image is malformed */
errFull error = errno(13) // /* Insertion failed because database is full */
errCantOpen error = errno(14) // /* Unable to open the database file */
errEmpty error = errno(16) // /* Database is empty */
errSchema error = errno(17) // /* The database schema changed */
errTooBig error = errno(18) // /* String or BLOB exceeds size limit */
errConstraint error = errno(19) // /* Abort due to constraint violation */
errMismatch error = errno(20) // /* Data type mismatch */
errMisuse error = errno(21) // /* Library used incorrectly */
errNolfs error = errno(22) // /* Uses OS features not supported on host */
errAuth error = errno(23) // /* Authorization denied */
errFormat error = errno(24) // /* Auxiliary database format error */
errRange error = errno(25) // /* 2nd parameter to sqlite3_bind out of range */
errNotDB error = errno(26) // /* File opened that is not a database file */
stepRow = errno(100) // /* sqlite3_step() has another row ready */
stepDone = errno(101) // /* sqlite3_step() has finished executing */
)
var errText = map[errno]string{
1: "SQL error or missing database",
2: "Internal logic error in SQLite",
3: "Access permission denied",
4: "Callback routine requested an abort",
5: "The database file is locked",
6: "A table in the database is locked",
7: "A malloc() failed",
8: "Attempt to write a readonly database",
9: "Operation terminated by sqlite3_interrupt()*/",
10: "Some kind of disk I/O error occurred",
11: "The database disk image is malformed",
12: "NOT USED. Table or record not found",
13: "Insertion failed because database is full",
14: "Unable to open the database file",
15: "NOT USED. Database lock protocol error",
16: "Database is empty",
17: "The database schema changed",
18: "String or BLOB exceeds size limit",
19: "Abort due to constraint violation",
20: "Data type mismatch",
21: "Library used incorrectly",
22: "Uses OS features not supported on host",
23: "Authorization denied",
24: "Auxiliary database format error",
25: "2nd parameter to sqlite3_bind out of range",
26: "File opened that is not a database file",
100: "sqlite3_step() has another row ready",
101: "sqlite3_step() has finished executing",
}
type impl struct{}
func (impl) Open(name string) (driver.Conn, error) {
if C.sqlite3_threadsafe() == 0 {
return nil, errors.New("sqlite library was not compiled for thread-safe operation")
}
var db *C.sqlite3
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
rv := C.sqlite3_open_v2(cname, &db,
C.SQLITE_OPEN_FULLMUTEX|
C.SQLITE_OPEN_READWRITE|
C.SQLITE_OPEN_CREATE,
nil)
if rv != 0 {
return nil, errno(rv)
}
if db == nil {
return nil, errors.New("sqlite succeeded without returning a database")
}
return &conn{db: db}, nil
}
type conn struct {
db *C.sqlite3
closed bool
tx bool
}
func (c *conn) error(rv C.int) error {
if rv == 0 {
return nil
}
if rv == 21 || c.closed {
return errno(rv)
}
return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
}
func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
if c.closed {
panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
}
cmdstr := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdstr))
var s *C.sqlite3_stmt
var tail *C.char
rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
if rv != 0 {
return nil, c.error(rv)
}
return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
}
func (c *conn) Close() error {
if c.closed {
panic("database/sql/driver: misuse of sqlite driver: multiple Close")
}
c.closed = true
rv := C.sqlite3_close(c.db)
c.db = nil
return c.error(rv)
}
func (c *conn) exec(cmd string) error {
cstring := C.CString(cmd)
defer C.free(unsafe.Pointer(cstring))
rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
return c.error(rv)
}
func (c *conn) Begin() (driver.Tx, error) {
if c.tx {
panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
}
if err := c.exec("BEGIN TRANSACTION"); err != nil {
return nil, err
}
c.tx = true
return &tx{c}, nil
}
type tx struct {
c *conn
}
func (t *tx) Commit() error {
if t.c == nil || !t.c.tx {
panic("database/sql/driver: misuse of sqlite driver: extra Commit")
}
t.c.tx = false
err := t.c.exec("COMMIT TRANSACTION")
t.c = nil
return err
}
func (t *tx) Rollback() error {
if t.c == nil || !t.c.tx {
panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
}
t.c.tx = false
err := t.c.exec("ROLLBACK")
t.c = nil
return err
}
type stmt struct {
c *conn
stmt *C.sqlite3_stmt
err error
t0 time.Time
sql string
args string
closed bool
rows bool
colnames []string
coltypes []string
}
func (s *stmt) Close() error {
if s.rows {
panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
}
if s.closed {
panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
}
s.closed = true
rv := C.sqlite3_finalize(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
return nil
}
func (s *stmt) NumInput() int {
if s.closed {
panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
}
return int(C.sqlite3_bind_parameter_count(s.stmt))
}
func (s *stmt) reset() error {
return s.c.error(C.sqlite3_reset(s.stmt))
}
func (s *stmt) start(args []driver.Value) error {
if err := s.reset(); err != nil {
return err
}
n := int(C.sqlite3_bind_parameter_count(s.stmt))
if n != len(args) {
return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
}
for i, v := range args {
var str string
switch v := v.(type) {
case nil:
if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
return s.c.error(rv)
}
continue
case float64:
if rv := C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)); rv != 0 {
return s.c.error(rv)
}
continue
case int64:
if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)); rv != 0 {
return s.c.error(rv)
}
continue
case []byte:
var p *byte
if len(v) > 0 {
p = &v[0]
}
if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
return s.c.error(rv)
}
continue
case bool:
var vi int64
if v {
vi = 1
}
if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
return s.c.error(rv)
}
continue
case time.Time:
str = v.UTC().Format(timefmt[0])
case string:
str = v
default:
str = fmt.Sprint(v)
}
cstr := C.CString(str)
rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
C.free(unsafe.Pointer(cstr))
if rv != 0 {
return s.c.error(rv)
}
}
return nil
}
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
if s.closed {
panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
}
if s.rows {
panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
}
err := s.start(args)
if err != nil {
return nil, err
}
rv := C.sqlite3_step(s.stmt)
if errno(rv) != stepDone {
if rv == 0 {
rv = 21 // errMisuse
}
return nil, s.c.error(rv)
}
id := int64(C.sqlite3_last_insert_rowid(s.c.db))
rows := int64(C.sqlite3_changes(s.c.db))
return &result{id, rows}, nil
}
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
if s.closed {
panic("database/sql/driver: misuse of sqlite driver: Query after Close")
}
if s.rows {
panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
}
err := s.start(args)
if err != nil {
return nil, err
}
s.rows = true
if s.colnames == nil {
n := int64(C.sqlite3_column_count(s.stmt))
s.colnames = make([]string, n)
s.coltypes = make([]string, n)
for i := range s.colnames {
s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
}
}
return &rows{s}, nil
}
type rows struct {
s *stmt
}
func (r *rows) Columns() []string {
if r.s == nil {
panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
}
return r.s.colnames
}
const maxslice = 1<<31 - 1
var timefmt = []string{
"2006-01-02 15:04:05.999999999",
"2006-01-02T15:04:05.999999999",
"2006-01-02 15:04:05",
"2006-01-02T15:04:05",
"2006-01-02 15:04",
"2006-01-02T15:04",
"2006-01-02",
}
func (r *rows) Next(dst []driver.Value) error {
if r.s == nil {
panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
}
rv := C.sqlite3_step(r.s.stmt)
if errno(rv) != stepRow {
if errno(rv) == stepDone {
return io.EOF
}
if rv == 0 {
rv = 21
}
return r.s.c.error(rv)
}
for i := range dst {
switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
default:
return fmt.Errorf("unexpected sqlite3 column type %d", typ)
case C.SQLITE_INTEGER:
val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
switch r.s.coltypes[i] {
case "timestamp", "datetime":
dst[i] = time.Unix(val, 0).UTC()
case "boolean":
dst[i] = val > 0
default:
dst[i] = val
}
case C.SQLITE_FLOAT:
dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
case C.SQLITE_BLOB, C.SQLITE_TEXT:
n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
var b []byte
if n > 0 {
p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
}
dst[i] = b
switch r.s.coltypes[i] {
case "timestamp", "datetime":
dst[i] = time.Time{}
s := string(b)
for _, f := range timefmt {
if t, err := time.Parse(f, s); err == nil {
dst[i] = t
break
}
}
}
case C.SQLITE_NULL:
dst[i] = nil
}
}
return nil
}
func (r *rows) Close() error {
if r.s == nil {
panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
}
r.s.rows = false
r.s = nil
return nil
}
type result struct {
id int64
rows int64
}
func (r *result) LastInsertId() (int64, error) {
return r.id, nil
}
func (r *result) RowsAffected() (int64, error) {
return r.rows, nil
}