commit 6c2d2c4b6b7ae45782a575d21b44892158cf447b Author: mattn Date: Fri Nov 11 21:36:22 2011 +0900 first import. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f78cd9 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +include $(GOROOT)/src/Make.inc + +TARG = github.com/mattn/go-sqlite3 +CGOFILES = sqlite3.go + +include $(GOROOT)/src/Make.pkg diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..0d12cfc --- /dev/null +++ b/example/Makefile @@ -0,0 +1,6 @@ +include $(GOROOT)/src/Make.inc + +TARG = main +GOFILES = main.go + +include $(GOROOT)/src/Make.cmd diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..5e28a57 --- /dev/null +++ b/example/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "exp/sql" + "fmt" + "os" + _ "github.com/mattn/go-sqlite3" +) + +func main() { + os.Remove("./foo.db") + + db, err := sql.Open("sqlite3", "./foo.db") + if err != nil { + fmt.Println(err) + return + } + + sqls := []string { + "create table foo (id integer not null primary key, name text)", + "delete from foo", + } + for _, sql := range(sqls) { + _, err = db.Exec(sql) + if err != nil { + fmt.Printf("%q: %s\n", err, sql) + return + } + } + + stmt, err := db.Prepare("insert into foo(id, name) values(?, ?)") + if err != nil { + fmt.Println(err) + return + } + defer stmt.Close() + + for i := 0; i < 100; i++ { + _, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i)) + if err != nil { + fmt.Println(err) + return + } + } + + rows, err := db.Query("select id, name from foo") + if err != nil { + fmt.Println(err) + return + } + defer rows.Close() + + for rows.Next() { + var id int + var name string + rows.Scan(&id, &name) + println(id, name) + } + +} 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 +#include +#include + +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 +}