| // 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 |
| } |