aboutsummaryrefslogtreecommitdiff
path: root/sqlite3.go
diff options
context:
space:
mode:
authormattn <mattn.jp@gmail.com>2011-11-11 21:36:22 +0900
committermattn <mattn.jp@gmail.com>2011-11-11 21:36:22 +0900
commit6c2d2c4b6b7ae45782a575d21b44892158cf447b (patch)
tree3723b57fa013d30ab191aa95d901dabc3ce632f6 /sqlite3.go
downloadgolite-6c2d2c4b6b7ae45782a575d21b44892158cf447b.tar.gz
golite-6c2d2c4b6b7ae45782a575d21b44892158cf447b.tar.xz
first import.
Diffstat (limited to 'sqlite3.go')
-rw-r--r--sqlite3.go253
1 files changed, 253 insertions, 0 deletions
diff --git a/sqlite3.go b/sqlite3.go
new file mode 100644
index 0000000..502e354
--- /dev/null
+++ b/sqlite3.go
@@ -0,0 +1,253 @@
+package sqlite
+
+/*
+#include <sqlite3.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
+ return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+
+static int
+_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
+ return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
+}
+
+#cgo LDFLAGS: -lsqlite3
+*/
+import "C"
+import (
+ "errors"
+ "exp/sql"
+ "exp/sql/driver"
+ //"reflect"
+ "unsafe"
+)
+
+func init() {
+ sql.Register("sqlite3", &SQLiteDriver{})
+}
+
+type SQLiteDriver struct {
+}
+
+type SQLiteConn struct {
+ db *C.sqlite3
+}
+
+type SQLiteTx struct {
+ c *SQLiteConn
+}
+
+func (tx *SQLiteTx) Commit() error {
+ if err := tx.c.exec("COMMIT"); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (tx *SQLiteTx) Rollback() error {
+ if err := tx.c.exec("ROLLBACK"); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *SQLiteConn) exec(cmd string) error {
+ pcmd := C.CString(cmd)
+ defer C.free(unsafe.Pointer(pcmd))
+ rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil)
+ if rv != C.SQLITE_OK {
+ return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
+ }
+ return nil
+}
+
+func (c *SQLiteConn) Begin() (driver.Tx, error) {
+ if err := c.exec("BEGIN"); err != nil {
+ return nil, err
+ }
+ return &SQLiteTx{c}, nil
+}
+
+func (d *SQLiteDriver) Open(dsn 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
+ name := C.CString(dsn)
+ defer C.free(unsafe.Pointer(name))
+ rv := C.sqlite3_open_v2(name, &db,
+ C.SQLITE_OPEN_FULLMUTEX|
+ C.SQLITE_OPEN_READWRITE|
+ C.SQLITE_OPEN_CREATE,
+ nil)
+ if rv != 0 {
+ return nil, errors.New(C.GoString(C.sqlite3_errmsg(db)))
+ }
+ if db == nil {
+ return nil, errors.New("sqlite succeeded without returning a database")
+ }
+ return &SQLiteConn{db}, nil
+}
+
+func (c *SQLiteConn) Close() error {
+ s := C.sqlite3_next_stmt(c.db, nil)
+ for s != nil {
+ C.sqlite3_finalize(s)
+ s = C.sqlite3_next_stmt(c.db, s)
+ }
+ rv := C.sqlite3_close(c.db)
+ if rv != C.SQLITE_OK {
+ return errors.New("sqlite succeeded without returning a database")
+ }
+ c.db = nil
+ return nil
+}
+
+type SQLiteStmt struct {
+ c *SQLiteConn
+ s *C.sqlite3_stmt
+ t string
+}
+
+func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
+ pquery := C.CString(query)
+ defer C.free(unsafe.Pointer(pquery))
+ var s *C.sqlite3_stmt
+ var perror *C.char
+ rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &perror)
+ if rv != C.SQLITE_OK {
+ return nil, errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
+ }
+ var t string
+ if perror != nil && C.strlen(perror) > 0 {
+ t = C.GoString(perror)
+ }
+ return &SQLiteStmt{c, s, t}, nil
+}
+
+func (s *SQLiteStmt) Close() error {
+ rv := C.sqlite3_finalize(s.s);
+ if rv != C.SQLITE_OK {
+ return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db)))
+ }
+ return nil
+}
+
+func (s *SQLiteStmt) NumInput() int {
+ return int(C.sqlite3_bind_parameter_count(s.s))
+}
+
+func (s *SQLiteStmt) bind(args []interface{}) error {
+ rv := C.sqlite3_reset(s.s)
+ if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv!= C.SQLITE_DONE {
+ return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db)))
+ }
+
+ for i, v := range args {
+ n := C.int(i+1)
+ switch v := v.(type) {
+ case nil:
+ rv = C.sqlite3_bind_null(s.s, n)
+ case string:
+ b := []byte(v)
+ rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
+ case int:
+ rv = C.sqlite3_bind_int(s.s, n, C.int(v))
+ case int64:
+ rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v))
+ case byte:
+ rv = C.sqlite3_bind_int(s.s, n, C.int(v))
+ case bool:
+ if bool(v) {
+ rv = C.sqlite3_bind_int(s.s, n, -1)
+ } else {
+ rv = C.sqlite3_bind_int(s.s, n, 0)
+ }
+ case float32:
+ rv = C.sqlite3_bind_double(s.s, n, C.double(v))
+ case float64:
+ rv = C.sqlite3_bind_double(s.s, n, C.double(v))
+ case []byte:
+ var p *byte
+ if len(v) > 0 {
+ p = &v[0]
+ }
+ rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(p), C.int(len(v)))
+ }
+ if rv != C.SQLITE_OK {
+ return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db)))
+ }
+ }
+ return nil
+}
+
+func (s *SQLiteStmt) Query(args []interface{}) (driver.Rows, error) {
+ if err := s.bind(args); err != nil {
+ return nil, err
+ }
+ return &SQLiteRows{s, int(C.sqlite3_column_count(s.s)), nil}, nil
+}
+
+func (s *SQLiteStmt) Exec(args []interface{}) (driver.Result, error) {
+ if err := s.bind(args); err != nil {
+ return nil, err
+ }
+ rv := C.sqlite3_step(s.s)
+ if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv!= C.SQLITE_DONE {
+ return nil, errors.New(C.GoString(C.sqlite3_errmsg(s.c.db)))
+ }
+ return driver.DDLSuccess, nil
+}
+
+type SQLiteRows struct {
+ s *SQLiteStmt
+ nc int
+ cols []string
+}
+
+func (rc *SQLiteRows) Close() error {
+ rv := C.sqlite3_finalize(rc.s.s)
+ if rv != C.SQLITE_OK {
+ return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db)))
+ }
+ return nil
+}
+
+func (rc *SQLiteRows) Columns() []string {
+ if rc.nc != len(rc.cols) {
+ rc.cols = make([]string, rc.nc)
+ for i := 0; i < rc.nc; i++ {
+ rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i)))
+ }
+ }
+ return rc.cols
+}
+
+func (rc *SQLiteRows) Next(dest []interface{}) error {
+ rv := C.sqlite3_step(rc.s.s)
+ if rv != C.SQLITE_ROW {
+ return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db)))
+ }
+ for i := range dest {
+ switch (C.sqlite3_column_type(rc.s.s, C.int(i))) {
+ case C.SQLITE_INTEGER:
+ dest[i] = int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
+ case C.SQLITE_FLOAT:
+ dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i)))
+ case C.SQLITE_BLOB:
+ n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
+ p := C.sqlite3_column_blob(rc.s.s, C.int(i))
+ dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
+ case C.SQLITE_NULL:
+ dest[i] = nil
+ case C.SQLITE_TEXT:
+ dest[i] = C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))))
+ }
+ }
+ return nil
+}