diff --git a/README.md b/README.md index abaef1d22c4c76a9f9946c1afe4f7a9aec3cb0d5..7b1a9f71a671fe9540da296269a2ddce53ffa0b7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ See [package documentation](http://godoc.org/github.com/gwenn/gosqlite). [1]: https://secure.travis-ci.org/gwenn/gosqlite.png [2]: http://www.travis-ci.org/gwenn/gosqlite +### Features (not supported by database/sql/driver): + +* Dynamic type: currently, the SQLite3 manifest typing is respected. There is no use of the column declared type to guess the target/go type when scanning. On your side, you should try to not break column affinity rules (such as declaring a column with TIMESTAMP type (NUMERIC affinity) storing values with '2006-01-02T15:04:05.999Z07:00' format (TEXT type))... +* Partial scan: scan values may be partially scanned (by index or name) or skipped/ignored by passing nil pointer(s). +* Null value: by default, empty string and zero time are bound to null for prepared statement's parameters (no need for NullString, NullTime but still supported). + ### Changes: Open supports flags. @@ -84,6 +90,7 @@ Time: JulianDay JulianDayToUTC JulianDayToLocalTime +UnixTime, JulianTime and TimeStamp used to persist go time in formats supported by SQLite3 date functions. Trace: Conn.BusyHandler diff --git a/driver.go b/driver.go index b53e912a913565d5d38adb49f8a37cc77a410485..1abd1b1876c308862a0740c1a687ee8bf7c930f8 100644 --- a/driver.go +++ b/driver.go @@ -46,6 +46,7 @@ type rowsImpl struct { // ":memory:" for memory db, // "" for temp file db func (d *impl) Open(name string) (driver.Conn, error) { + // OpenNoMutex == multi-thread mode (http://sqlite.org/compile.html#threadsafe and http://sqlite.org/threadsafe.html) c, err := Open(name, OpenUri, OpenNoMutex, OpenReadWrite, OpenCreate) if err != nil { return nil, err diff --git a/meta.go b/meta.go index 0df730f0a2b2411c46bbec0d2daf5e00a0375faf..614653fcabfdc7dd9c9168368199de27ba545e33 100644 --- a/meta.go +++ b/meta.go @@ -8,14 +8,6 @@ package sqlite #include <sqlite3.h> #include <stdlib.h> -// cgo doesn't support varargs -static char *my_mprintf(char *zFormat, char *arg) { - return sqlite3_mprintf(zFormat, arg); -} -static char *my_mprintf2(char *zFormat, char *arg1, char *arg2) { - return sqlite3_mprintf(zFormat, arg1, arg2); -} - // just to get ride of warning static int my_table_column_metadata( sqlite3 *db, @@ -299,34 +291,3 @@ func (c *Conn) IndexColumns(dbName, index string) ([]Column, error) { } return columns, nil } - -// Mprintf is like fmt.Printf but implements some additional formatting options -// that are useful for constructing SQL statements. -// (See http://sqlite.org/c3ref/mprintf.html) -func Mprintf(format string, arg string) string { - zSQL := mPrintf(format, arg) - defer C.sqlite3_free(unsafe.Pointer(zSQL)) - return C.GoString(zSQL) -} -func mPrintf(format, arg string) *C.char { - cf := C.CString(format) - defer C.free(unsafe.Pointer(cf)) - ca := C.CString(arg) - defer C.free(unsafe.Pointer(ca)) - return C.my_mprintf(cf, ca) -} - -// Mprintf2 is like fmt.Printf but implements some additional formatting options -// that are useful for constructing SQL statements. -// (See http://sqlite.org/c3ref/mprintf.html) -func Mprintf2(format string, arg1, arg2 string) string { - cf := C.CString(format) - defer C.free(unsafe.Pointer(cf)) - ca1 := C.CString(arg1) - defer C.free(unsafe.Pointer(ca1)) - ca2 := C.CString(arg2) - defer C.free(unsafe.Pointer(ca2)) - zSQL := C.my_mprintf2(cf, ca1, ca2) - defer C.sqlite3_free(unsafe.Pointer(zSQL)) - return C.GoString(zSQL) -} diff --git a/sqlite.go b/sqlite.go index c83c771d75cbd08f07cfd0f62b52327e781c7d11..4bf73ab4e6120564f59bcc6e6cb2198ae9f1dec1 100644 --- a/sqlite.go +++ b/sqlite.go @@ -23,7 +23,6 @@ import ( "fmt" "io" "os" - "reflect" "strconv" "time" "unsafe" @@ -202,14 +201,14 @@ func OpenVfs(filename string, vfsname string, flags ...OpenFlag) (*Conn, error) } var db *C.sqlite3 - name := C.CString(filename) - defer C.free(unsafe.Pointer(name)) + cname := C.CString(filename) + defer C.free(unsafe.Pointer(cname)) var vfs *C.char if len(vfsname) > 0 { vfs = C.CString(vfsname) defer C.free(unsafe.Pointer(vfs)) } - rv := C.sqlite3_open_v2(name, &db, C.int(openFlags), vfs) + rv := C.sqlite3_open_v2(cname, &db, C.int(openFlags), vfs) if rv != C.SQLITE_OK { if db != nil { C.sqlite3_close(db) @@ -352,6 +351,7 @@ func (c *Conn) Exists(query string, args ...interface{}) (bool, error) { // OneValue is used with SELECT that returns only one row with only one column. // Returns io.EOF when there is no row. +// No check is performed to ensure that there is no more than one row. func (c *Conn) OneValue(query string, value interface{}, args ...interface{}) error { s, err := c.Prepare(query, args...) if err != nil { @@ -591,23 +591,3 @@ func EnableSharedCache(b bool) error { } return Errno(rv) } - -// Must is a helper that wraps a call to a function returning (bool, os.Error) -// and panics if the error is non-nil. -func Must(b bool, err error) bool { - if err != nil { - panic(err) - } - return b -} - -func btocint(b bool) C.int { - if b { - return 1 - } - return 0 -} -func cstring(s string) (*C.char, C.int) { - cs := *(*reflect.StringHeader)(unsafe.Pointer(&s)) - return (*C.char)(unsafe.Pointer(cs.Data)), C.int(cs.Len) -} diff --git a/stmt.go b/stmt.go index 48cd14888b64ad89812888a2609afc4c106c27fc..3fd8cbd4455e013253ec308a67cc685e02d9032f 100644 --- a/stmt.go +++ b/stmt.go @@ -297,7 +297,7 @@ func (s *Stmt) NamedBind(args ...interface{}) error { } // Bind binds parameters by their index. -// Calls sqlite3_bind_parameter_count and sqlite3_bind_(blob|double|int|int64|null|text) depending on args type. +// Calls sqlite3_bind_parameter_count and sqlite3_bind_(blob|double|int|int64|null|text) depending on args type/kind. // (See http://sqlite.org/c3ref/bind_blob.html) func (s *Stmt) Bind(args ...interface{}) error { n := s.BindParameterCount() @@ -321,6 +321,7 @@ var NullIfEmptyString = true var NullIfZeroTime = true // BindByIndex binds value to the specified host parameter of the prepared statement. +// Value's type/kind is used to find the storage class. // The leftmost SQL parameter has an index of 1. func (s *Stmt) BindByIndex(index int, value interface{}) error { i := C.int(index) @@ -373,6 +374,9 @@ func (s *Stmt) BindByIndex(index int, value interface{}) error { return s.error(rv, "Stmt.Bind") } +// BindReflect binds value to the specified host parameter of the prepared statement. +// Value's (reflect) Kind is used to find the storage class. +// The leftmost SQL parameter has an index of 1. func (s *Stmt) BindReflect(index int, value interface{}) error { i := C.int(index) var rv C.int @@ -423,7 +427,7 @@ func (s *Stmt) Next() (bool, error) { if err == Row { return true, nil } - C.sqlite3_reset(s.stmt) + C.sqlite3_reset(s.stmt) // Release implicit lock as soon as possible (see dbEvalStep in tclsqlite3.c) if err != Done { return false, s.error(rv, "Stmt.Next") } @@ -543,7 +547,7 @@ func (s *Stmt) NamedScan(args ...interface{}) error { // // NULL value is converted to 0 if arg type is *int,*int64,*float,*float64, to "" for *string, to []byte{} for *[]byte and to false for *bool. // To avoid NULL conversion, arg type must be **T. -// Calls sqlite3_column_(blob|double|int|int64|text) depending on args type. +// Calls sqlite3_column_(blob|double|int|int64|text) depending on args type/kind. // (See http://sqlite.org/c3ref/column_blob.html) func (s *Stmt) Scan(args ...interface{}) error { n := s.ColumnCount() @@ -590,7 +594,7 @@ func (s *Stmt) ColumnIndex(name string) (int, error) { // ScanByName scans result value from a query. // Returns true when column is null. -// Calls sqlite3_column_(blob|double|int|int64|text) depending on arg type. +// Calls sqlite3_column_(blob|double|int|int64|text) depending on arg type/kind. // (See http://sqlite.org/c3ref/column_blob.html) func (s *Stmt) ScanByName(name string, value interface{}) (bool, error) { index, err := s.ColumnIndex(name) @@ -604,18 +608,19 @@ func (s *Stmt) ScanByName(name string, value interface{}) (bool, error) { // The leftmost column/index is number 0. // // Destination type is specified by the caller (except when value type is *interface{}). -// The value must be of one of the following types: -// (*)*string, -// (*)*int, (*)*int64, (*)*byte, +// The value must be of one of the following types/kinds: +// (*)*string +// (*)*int,int8,int16,int32,int64 +// (*)*uint,uint8,uint16,uint32,uint64 // (*)*bool -// (*)*float64 +// (*)*float32,float64 // (*)*[]byte // *time.Time // sql.Scanner // *interface{} // // Returns true when column is null. -// Calls sqlite3_column_(blob|double|int|int64|text) depending on arg type. +// Calls sqlite3_column_(blob|double|int|int64|text) depending on arg type/kind. // (See http://sqlite.org/c3ref/column_blob.html) func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) { var isNull bool @@ -716,6 +721,18 @@ func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) { return isNull, err } +// ScanReflect scans result value from a query. +// The leftmost column/index is number 0. +// +// Destination type is specified by the caller. +// The value must be of one of the following kinds: +// *string +// *int,int8,int16,int32,int64 +// *uint,uint8,uint16,uint32,uint64 +// *bool +// *float32,float64 +// +// Returns true when column is null. func (s *Stmt) ScanReflect(index int, v interface{}) (bool, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr || rv.IsNil() { diff --git a/util.go b/util.go new file mode 100644 index 0000000000000000000000000000000000000000..976b183ce15a9d62855b5aeb091cf73695040f77 --- /dev/null +++ b/util.go @@ -0,0 +1,84 @@ +// 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 sqlite + +/* +#include <sqlite3.h> +#include <stdlib.h> + +// cgo doesn't support varargs +static char *my_mprintf(char *zFormat, char *arg) { + return sqlite3_mprintf(zFormat, arg); +} +static char *my_mprintf2(char *zFormat, char *arg1, char *arg2) { + return sqlite3_mprintf(zFormat, arg1, arg2); +} +*/ +import "C" + +import ( + "reflect" + "unsafe" +) + +// Mprintf is like fmt.Printf but implements some additional formatting options +// that are useful for constructing SQL statements. +// (See http://sqlite.org/c3ref/mprintf.html) +func Mprintf(format string, arg string) string { + zSQL := mPrintf(format, arg) + defer C.sqlite3_free(unsafe.Pointer(zSQL)) + return C.GoString(zSQL) +} +func mPrintf(format, arg string) *C.char { + cf := C.CString(format) + defer C.free(unsafe.Pointer(cf)) + ca := C.CString(arg) + defer C.free(unsafe.Pointer(ca)) + return C.my_mprintf(cf, ca) +} + +// Mprintf2 is like fmt.Printf but implements some additional formatting options +// that are useful for constructing SQL statements. +// (See http://sqlite.org/c3ref/mprintf.html) +func Mprintf2(format string, arg1, arg2 string) string { + cf := C.CString(format) + defer C.free(unsafe.Pointer(cf)) + ca1 := C.CString(arg1) + defer C.free(unsafe.Pointer(ca1)) + ca2 := C.CString(arg2) + defer C.free(unsafe.Pointer(ca2)) + zSQL := C.my_mprintf2(cf, ca1, ca2) + defer C.sqlite3_free(unsafe.Pointer(zSQL)) + return C.GoString(zSQL) +} + +// Must is a helper that wraps a call to a function returning (bool, os.Error) +// and panics if the error is non-nil. +func Must(b bool, err error) bool { + if err != nil { + panic(err) + } + return b +} + +func btocint(b bool) C.int { + if b { + return 1 + } + return 0 +} +func cstring(s string) (*C.char, C.int) { + cs := *(*reflect.StringHeader)(unsafe.Pointer(&s)) + return (*C.char)(unsafe.Pointer(cs.Data)), C.int(cs.Len) +} + +/* +func gostring(cs *C.char) string { + var x reflect.StringHeader + x.Data = uintptr(unsafe.Pointer(cs)) + x.Len = int(C.strlen(cs)) + return *(*string)(unsafe.Pointer(&x)) +} +*/