Commit 67fc1ed7 authored by gwenn's avatar gwenn

Customize to my needs.

parent 8e81aa1b
*.6
*.8
*.swp
*~
*.[568ao]
[568a].out
_testmain.go
_obj
*.o
*.out
*.cgo*
_test
cgo_*
_cgo_*
_testmain.go
*.swp
......@@ -4,15 +4,9 @@
include $(GOROOT)/src/Make.inc
TARG=fsqlite
TARG=github.com/gwenn/sqlite
CGOFILES=\
fsqlite.go
ifeq ($(GOOS),darwin)
CGO_LDFLAGS=/usr/lib/libsqlite3.0.dylib
else
CGO_LDFLAGS=-lsqlite3
endif
sqlite.go
include $(GOROOT)/src/Make.pkg
This is based on Russ Cox's original gosqlite package:
Yet another SQLite binding based on:
- original [Russ Cox's][http://code.google.com/p/gosqlite/] implementation,
- the [Patrick Crosby's][https://github.com/patrickxb/fgosqlite/] fork.
http://code.google.com/p/gosqlite/
I added Scan2 and Exec2 which use sqlite api calls to get/bind values instead of the
original which basically converted everything to strings.
Scan2 and Exec2 are significantly faster. See the benchmarks in fgosqlite_test.go.
Conn#Exec handles multiple statements (separated by semicolons) properly.
Stmt#Exec is renamed in Stmt#Bind and a new Stmt#Exec method is introduced to #Bind and #Step.
Stmt#Bind uses native sqlite3_bind_x methods and failed if unsupported type.
Stmt#Next returns a (bool, os.Error) couple like Reader#Read. But its use in for loop becomes inelegant...
Stmt#Scan uses native sqlite3_column_x methods.
Stmt#NamedScan is added. It's compliant with [go-dbi][https://github.com/thomaslee/go-dbi/] API but I think its signature should be improved/modified.
......@@ -3,11 +3,14 @@
// license that can be found in the LICENSE file.
// Package sqlite provides access to the SQLite library, version 3.
package fsqlite
package sqlite
/*
#cgo LDFLAGS: -lsqlite3
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
// These wrappers are necessary because SQLITE_TRANSIENT
// is a pointer constant, and cgo doesn't translate them correctly.
......@@ -18,10 +21,10 @@ package fsqlite
// #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);
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);
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
}
*/
......@@ -31,9 +34,8 @@ import (
"fmt"
"os"
"reflect"
"strconv"
"unsafe"
"time"
"unsafe"
)
type Errno int
......@@ -41,38 +43,38 @@ type Errno int
func (e Errno) String() string {
s := errText[e]
if s == "" {
return fmt.Sprintf("errno %d", e)
return fmt.Sprintf("errno %d", int(e))
}
return s
}
var (
ErrError os.Error = Errno(1) // /* SQL error or missing database */
ErrInternal os.Error = Errno(2) // /* Internal logic error in SQLite */
ErrPerm os.Error = Errno(3) // /* Access permission denied */
ErrAbort os.Error = Errno(4) // /* Callback routine requested an abort */
ErrBusy os.Error = Errno(5) // /* The database file is locked */
ErrLocked os.Error = Errno(6) // /* A table in the database is locked */
ErrNoMem os.Error = Errno(7) // /* A malloc() failed */
ErrReadOnly os.Error = Errno(8) // /* Attempt to write a readonly database */
ErrInterrupt os.Error = Errno(9) // /* Operation terminated by sqlite3_interrupt()*/
ErrIOErr os.Error = Errno(10) // /* Some kind of disk I/O error occurred */
ErrCorrupt os.Error = Errno(11) // /* The database disk image is malformed */
ErrFull os.Error = Errno(13) // /* Insertion failed because database is full */
ErrCantOpen os.Error = Errno(14) // /* Unable to open the database file */
ErrEmpty os.Error = Errno(16) // /* Database is empty */
ErrSchema os.Error = Errno(17) // /* The database schema changed */
ErrTooBig os.Error = Errno(18) // /* String or BLOB exceeds size limit */
ErrConstraint os.Error = Errno(19) // /* Abort due to constraint violation */
ErrMismatch os.Error = Errno(20) // /* Data type mismatch */
ErrMisuse os.Error = Errno(21) // /* Library used incorrectly */
ErrNolfs os.Error = Errno(22) // /* Uses OS features not supported on host */
ErrAuth os.Error = Errno(23) // /* Authorization denied */
ErrFormat os.Error = Errno(24) // /* Auxiliary database format error */
ErrRange os.Error = Errno(25) // /* 2nd parameter to sqlite3_bind out of range */
ErrNotDB os.Error = Errno(26) // /* File opened that is not a database file */
Row = Errno(100) // /* sqlite3_step() has another row ready */
Done = Errno(101) // /* sqlite3_step() has finished executing */
ErrError os.Error = Errno(1) // /* SQL error or missing database */
ErrInternal os.Error = Errno(2) // /* Internal logic error in SQLite */
ErrPerm os.Error = Errno(3) // /* Access permission denied */
ErrAbort os.Error = Errno(4) // /* Callback routine requested an abort */
ErrBusy os.Error = Errno(5) // /* The database file is locked */
ErrLocked os.Error = Errno(6) // /* A table in the database is locked */
ErrNoMem os.Error = Errno(7) // /* A malloc() failed */
ErrReadOnly os.Error = Errno(8) // /* Attempt to write a readonly database */
ErrInterrupt os.Error = Errno(9) // /* Operation terminated by sqlite3_interrupt()*/
ErrIOErr os.Error = Errno(10) // /* Some kind of disk I/O error occurred */
ErrCorrupt os.Error = Errno(11) // /* The database disk image is malformed */
ErrFull os.Error = Errno(13) // /* Insertion failed because database is full */
ErrCantOpen os.Error = Errno(14) // /* Unable to open the database file */
ErrEmpty os.Error = Errno(16) // /* Database is empty */
ErrSchema os.Error = Errno(17) // /* The database schema changed */
ErrTooBig os.Error = Errno(18) // /* String or BLOB exceeds size limit */
ErrConstraint os.Error = Errno(19) // /* Abort due to constraint violation */
ErrMismatch os.Error = Errno(20) // /* Data type mismatch */
ErrMisuse os.Error = Errno(21) // /* Library used incorrectly */
ErrNolfs os.Error = Errno(22) // /* Uses OS features not supported on host */
ErrAuth os.Error = Errno(23) // /* Authorization denied */
ErrFormat os.Error = Errno(24) // /* Auxiliary database format error */
ErrRange os.Error = Errno(25) // /* 2nd parameter to sqlite3_bind out of range */
ErrNotDB os.Error = Errno(26) // /* File opened that is not a database file */
Row = Errno(100) // /* sqlite3_step() has another row ready */
Done = Errno(101) // /* sqlite3_step() has finished executing */
)
var errText = map[Errno]string{
......@@ -110,6 +112,9 @@ func (c *Conn) error(rv C.int) os.Error {
if c == nil || c.db == nil {
return os.NewError("nil sqlite database")
}
if rv == 0 {
return nil
}
if rv == 21 { // misuse
return Errno(rv)
}
......@@ -125,6 +130,16 @@ func Version() string {
return C.GoString(p)
}
func EnableSharedCache(b bool) os.Error {
rv := C.sqlite3_enable_shared_cache(btocint(b))
if rv != 0 {
return Errno(rv)
}
return nil
}
// ':memory:' for memory db
// '' for temp db
func Open(filename string) (*Conn, os.Error) {
if C.sqlite3_threadsafe() == 0 {
return nil, os.NewError("sqlite library was not compiled for thread-safe operation")
......@@ -136,9 +151,13 @@ func Open(filename string) (*Conn, os.Error) {
rv := C.sqlite3_open_v2(name, &db,
C.SQLITE_OPEN_FULLMUTEX|
C.SQLITE_OPEN_READWRITE|
C.SQLITE_OPEN_CREATE,
C.SQLITE_OPEN_CREATE|
C.SQLITE_OPEN_URI,
nil)
if rv != 0 {
if db != nil {
C.sqlite3_close(db)
}
return nil, Errno(rv)
}
if db == nil {
......@@ -147,30 +166,119 @@ func Open(filename string) (*Conn, os.Error) {
return &Conn{db}, nil
}
func (c *Conn) Exec(cmd string, args ...interface{}) os.Error {
s, err := c.Prepare(cmd)
if err != nil {
return err
func NewBackup(dst *Conn, dstTable string, src *Conn, srcTable string) (*Backup, os.Error) {
dname := C.CString(dstTable)
sname := C.CString(srcTable)
defer C.free(unsafe.Pointer(dname))
defer C.free(unsafe.Pointer(sname))
sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
if sb == nil {
return nil, dst.error(C.sqlite3_errcode(dst.db))
}
defer s.Finalize()
err = s.Exec(args...)
if err != nil {
return err
return &Backup{sb, dst, src}, nil
}
type Backup struct {
sb *C.sqlite3_backup
dst, src *Conn
}
func (b *Backup) Step(npage int) os.Error {
rv := C.sqlite3_backup_step(b.sb, C.int(npage))
if rv == 0 || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked {
return nil
}
rv := C.sqlite3_step(s.stmt)
if Errno(rv) != Done {
return c.error(rv)
return Errno(rv)
}
type BackupStatus struct {
Remaining int
PageCount int
}
func (b *Backup) Status() BackupStatus {
return BackupStatus{int(C.sqlite3_backup_remaining(b.sb)), int(C.sqlite3_backup_pagecount(b.sb))}
}
func (b *Backup) Run(npage int, sleepNs int64, c chan<- BackupStatus) os.Error {
var err os.Error
for {
err = b.Step(npage)
if err != nil {
break
}
if c != nil {
c <- b.Status()
}
time.Sleep(sleepNs)
}
return b.dst.error(C.sqlite3_errcode(b.dst.db))
}
func (b *Backup) Close() os.Error {
if b.sb == nil {
return os.EINVAL
}
C.sqlite3_backup_finish(b.sb)
b.sb = nil
return nil
}
func (c *Conn) BusyTimeout(ms int) os.Error {
rv := C.sqlite3_busy_timeout(c.db, C.int(ms))
if rv == 0 {
return nil
}
return Errno(rv)
}
// Don't use it with SELECT or anything that returns data.
func (c *Conn) Exec(cmd string, args ...interface{}) os.Error {
for len(cmd) > 0 {
s, err := c.Prepare(cmd)
if err != nil {
return err
} else if s.stmt == nil {
// this happens for a comment or white-space
cmd = s.tail
s.Finalize()
continue
}
err = s.Exec(args...)
if err != nil {
s.Finalize()
return err
}
if len(s.tail) > 0 {
if len(args) > 0 {
s.Finalize()
return os.NewError("Cannot execute multiple statements when args are specified")
}
}
cmd = s.tail
s.Finalize()
}
return nil
}
func (c *Conn) Changes() int {
return int(C.sqlite3_changes(c.db))
}
func (c *Conn) LastInsertRowid() int64 {
return int64(C.sqlite3_last_insert_rowid(c.db))
}
func (c *Conn) Interrupt() {
C.sqlite3_interrupt(c.db)
}
type Stmt struct {
c *Conn
stmt *C.sqlite3_stmt
err os.Error
t0 int64
sql string
args string
tail string
cols map[string]int // cached columns index by name
}
func (c *Conn) Prepare(cmd string) (*Stmt, os.Error) {
......@@ -181,114 +289,79 @@ func (c *Conn) Prepare(cmd string) (*Stmt, os.Error) {
defer C.free(unsafe.Pointer(cmdstr))
var stmt *C.sqlite3_stmt
var tail *C.char
rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &stmt, &tail)
rv := C.sqlite3_prepare_v2(c.db, cmdstr, -1, &stmt, &tail)
if rv != 0 {
return nil, c.error(rv)
}
return &Stmt{c: c, stmt: stmt, sql: cmd, t0: time.Nanoseconds()}, nil
var t string
if tail != nil && C.strlen(tail) > 0 {
t = C.GoString(tail)
}
return &Stmt{c: c, stmt: stmt, tail: t}, nil
}
// Don't use it with SELECT or anything that returns data.
func (s *Stmt) Exec(args ...interface{}) os.Error {
s.args = fmt.Sprintf(" %v", []interface{}(args))
rv := C.sqlite3_reset(s.stmt)
if rv != 0 {
return s.c.error(rv)
err := s.Bind(args...)
if err != nil {
return err
}
n := int(C.sqlite3_bind_parameter_count(s.stmt))
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
rv := C.sqlite3_step(s.stmt)
if Errno(rv) != Done {
return s.c.error(rv)
}
return nil
}
for i, v := range args {
var str string
switch v := v.(type) {
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:
if v {
str = "1"
} else {
str = "0"
}
default:
str = fmt.Sprint(v)
}
func (s *Stmt) BindParameterCount() int {
return int(C.sqlite3_bind_parameter_count(s.stmt))
}
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) BindParameterIndex(name string) int {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return int(C.sqlite3_bind_parameter_index(s.stmt, cname))
}
func (s *Stmt) Exec2(args ...interface{}) os.Error {
s.args = fmt.Sprintf(" %v", []interface{}(args))
func (s *Stmt) Bind(args ...interface{}) os.Error {
rv := C.sqlite3_reset(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
n := int(C.sqlite3_bind_parameter_count(s.stmt))
n := s.BindParameterCount()
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Bind: have %d want %d", len(args), n))
}
for i, v := range args {
var str string
index := C.int(i + 1)
switch v := v.(type) {
case nil:
rv = C.sqlite3_bind_null(s.stmt, index)
case string:
cstr := C.CString(v)
rv = C.my_bind_text(s.stmt, index, cstr, C.int(len(v)))
C.free(unsafe.Pointer(cstr))
case int:
if rv := C.sqlite3_bind_int(s.stmt, C.int(i+1), C.int(v)); rv != 0 {
return s.c.error(rv)
}
continue
rv = C.sqlite3_bind_int(s.stmt, index, C.int(v))
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
rv = C.sqlite3_bind_int64(s.stmt, index, C.sqlite3_int64(v))
case byte:
rv = C.sqlite3_bind_int(s.stmt, index, C.int(v))
case bool:
rv = C.sqlite3_bind_int(s.stmt, index, btocint(v))
case float32:
rv = C.sqlite3_bind_double(s.stmt, index, C.double(v))
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
rv = C.sqlite3_bind_double(s.stmt, index, C.double(v))
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:
if v {
str = "1"
} else {
str = "0"
}
default:
str = fmt.Sprint(v)
rv = C.my_bind_blob(s.stmt, index, unsafe.Pointer(p), C.int(len(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)
}
......@@ -296,130 +369,125 @@ func (s *Stmt) Exec2(args ...interface{}) os.Error {
return nil
}
func (s *Stmt) Error() os.Error {
return s.err
}
func (s *Stmt) Next() bool {
func (s *Stmt) Next() (bool, os.Error) {
rv := C.sqlite3_step(s.stmt)
err := Errno(rv)
if err == Row {
return true
return true, nil
}
if err != Done {
s.err = s.c.error(rv)
return false, s.c.error(rv)
}
return false
return false, nil
}
func (s *Stmt) Scan(args ...interface{}) os.Error {
n := int(C.sqlite3_column_count(s.stmt))
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
func (s *Stmt) Reset() os.Error {
rv := C.sqlite3_reset(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
return nil
}
for i, v := range args {
n := C.sqlite3_column_bytes(s.stmt, C.int(i))
p := C.sqlite3_column_blob(s.stmt, C.int(i))
if p == nil && n > 0 {
return os.NewError("got nil blob")
func (s *Stmt) ColumnCount() int {
return int(C.sqlite3_column_count(s.stmt))
}
func (s *Stmt) ColumnName(index int) string {
return C.GoString(C.sqlite3_column_name(s.stmt, C.int(index)))
}
func (s *Stmt) NamedScan(args ...interface{}) os.Error {
if len(args)%2 != 0 {
return os.NewError("Expected an even number of arguments")
}
for i := 0; i < len(args); i += 2 {
name, ok := args[i].(string)
if !ok {
return os.NewError("non-string field name field")
}
var data []byte
if n > 0 {
data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
index, err := s.fieldIndex(name) // How to look up only once for one statement ?
if err != nil {
return err
}
switch v := v.(type) {
case *[]byte:
*v = data
case *string:
*v = string(data)
case *bool:
*v = string(data) == "1"
case *int:
x, err := strconv.Atoi(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int: " + err.String())
}
*v = x
case *int64:
x, err := strconv.Atoi64(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int64: " + err.String())
}
*v = x
case *float64:
x, err := strconv.Atof64(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as float64: " + err.String())
}
*v = x
default:
return os.NewError("unsupported type in Scan: " + reflect.Typeof(v).String())
ptr := args[i+1]
err = s.scanField(index, ptr)
if err != nil {
return err
}
}
return nil
}
func (s *Stmt) Scan2(args ...interface{}) os.Error {
n := int(C.sqlite3_column_count(s.stmt))
func (s *Stmt) Scan(args ...interface{}) os.Error {
n := s.ColumnCount()
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
}
for i, v := range args {
/*
n := C.sqlite3_column_bytes(s.stmt, C.int(i))
p := C.sqlite3_column_blob(s.stmt, C.int(i))
if p == nil && n > 0 {
return os.NewError("got nil blob")
}
var data []byte
if n > 0 {
data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
}
*/
switch v := v.(type) {
/*
case *[]byte:
*v = data
case *string:
*v = string(data)
case *bool:
*v = string(data) == "1"
case *int:
x, err := strconv.Atoi(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int: " + err.String())
}
*v = x
*/
case *int64:
x := C.sqlite3_column_int64(s.stmt, C.int(i))
*v = int64(x)
case *float64:
x := C.sqlite3_column_double(s.stmt, C.int(i))
*v = float64(x)
default:
return os.NewError("unsupported type in Scan: " + reflect.Typeof(v).String())
err := s.scanField(i, v)
if err != nil {
return err
}
}
return nil
}
func (s *Stmt) SQL() string {
return s.sql + s.args
func (s *Stmt) fieldIndex(name string) (int, os.Error) {
if s.cols == nil {
count := s.ColumnCount()
s.cols = make(map[string]int, count)
for i := 0; i < count; i++ {
s.cols[s.ColumnName(i)] = i
}
}
index, ok := s.cols[name]
if ok {
return index, nil
}
return 0, os.NewError("invalid column name: " + name)
}
func (s *Stmt) Nanoseconds() int64 {
return time.Nanoseconds() - s.t0
func (s *Stmt) scanField(index int, value interface{}) os.Error {
switch value := value.(type) {
case *string:
p := C.sqlite3_column_text(s.stmt, C.int(index))
if p == nil {
value = nil
} else {
n := C.sqlite3_column_bytes(s.stmt, C.int(index))
*value = C.GoStringN((*C.char)(unsafe.Pointer(p)), n)
}
case *int:
*value = int(C.sqlite3_column_int(s.stmt, C.int(index)))
case *int64:
*value = int64(C.sqlite3_column_int64(s.stmt, C.int(index)))
case *byte:
*value = byte(C.sqlite3_column_int(s.stmt, C.int(index)))
case *bool:
*value = C.sqlite3_column_int(s.stmt, C.int(index)) == 1
case *float64:
*value = float64(C.sqlite3_column_double(s.stmt, C.int(index)))
case *[]byte:
p := C.sqlite3_column_blob(s.stmt, C.int(index))
if p == nil {
value = nil
} else {
n := C.sqlite3_column_bytes(s.stmt, C.int(index))
*value = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
}
default:
return os.NewError("unsupported type in Scan: " + reflect.TypeOf(value).String())
}
return nil
}
func (s *Stmt) Finalize() os.Error {
rv := C.sqlite3_finalize(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
s.stmt = nil
return nil
}
......@@ -434,3 +502,10 @@ func (c *Conn) Close() os.Error {
c.db = nil
return nil
}
func btocint(b bool) C.int {
if b {
return 1
}
return 0
}
package fsqlite
package sqlite
import (
"testing"
"fmt"
"os"
)
func TestOpen(t *testing.T) {
db, err := Open("/tmp/test.db")
func open(t *testing.T) *Conn {
db, err := Open("")
if err != nil {
t.Errorf("couldn't open database file: %s", err)
t.Fatalf("couldn't open database file: %s", err)
}
if db == nil {
t.Error("opened database is nil")
t.Fatal("opened database is nil")
}
db.Close()
return db
}
func TestCreateTable(t *testing.T) {
db, err := Open("/tmp/test.db")
db.Exec("DROP TABLE test")
err = db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
func createTable(db *Conn, t *testing.T) {
err := db.Exec("DROP TABLE IF EXISTS test;" +
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT," +
" float_num REAL, int_num INTEGER, a_string TEXT); -- bim")
if err != nil {
t.Errorf("error creating table: %s", err)
t.Fatalf("error creating table: %s", err)
}
}
type OutRow struct {
Key int64
FloatNum float64
IntNum int64
AString string
func TestOpen(t *testing.T) {
db := open(t)
db.Close()
}
func TestCreateTable(t *testing.T) {
db := open(t)
defer db.Close()
createTable(db, t)
}
func TestInsert(t *testing.T) {
db, _ := Open("/tmp/test.db")
db.Exec("DROP TABLE test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
db := open(t)
defer db.Close()
createTable(db, t)
for i := 0; i < 1000; i++ {
ierr := db.Exec("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)", float64(i)*float64(3.14), i, "hello")
if ierr != nil {
t.Errorf("insert error: %s", ierr)
t.Fatalf("insert error: %s", ierr)
}
c := db.Changes()
if c != 1 {
t.Errorf("insert error: %d <> 1", c)
}
}
cs, _ := db.Prepare("SELECT COUNT(*) FROM test")
cs.Exec()
if !cs.Next() {
t.Error("no result for count")
defer cs.Finalize()
if ok, err := cs.Next(); !ok {
if err != nil {
t.Fatalf("error preparing count: %s", err)
}
t.Fatal("no result for count")
}
var i int
err := cs.Scan(&i)
if err != nil {
t.Errorf("error scanning count: %s", err)
t.Fatalf("error scanning count: %s", err)
}
if i != 1000 {
t.Errorf("count should be 1000, but it is %d", i)
......@@ -59,169 +70,153 @@ func TestInsert(t *testing.T) {
}
func TestInsertWithStatement(t *testing.T) {
db, _ := Open("/tmp/test_is.db")
db.Exec("DROP TABLE test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
db := open(t)
defer db.Close()
createTable(db, t)
s, serr := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
if serr != nil {
t.Errorf("prepare error: %s", serr)
t.Fatalf("prepare error: %s", serr)
}
if s == nil {
t.Error("statement is nil")
t.Fatal("statement is nil")
}
defer s.Finalize()
for i := 0; i < 1000; i++ {
ierr := s.Exec(float64(i)*float64(3.14), i, "hello")
if ierr != nil {
t.Errorf("insert error: %s", ierr)
t.Fatalf("insert error: %s", ierr)
}
s.Next()
}
s.Finalize()
cs, _ := db.Prepare("SELECT COUNT(*) FROM test")
cs.Exec()
if !cs.Next() {
t.Error("no result for count")
}
var i int
err := cs.Scan(&i)
if err != nil {
t.Errorf("error scanning count: %s", err)
}
if i != 1000 {
t.Errorf("count should be 1000, but it is %d", i)
}
}
func TestInsertWithStatement2(t *testing.T) {
db, _ := Open("/tmp/test_is2.db")
db.Exec("DROP TABLE test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
s, serr := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
if serr != nil {
t.Errorf("prepare error: %s", serr)
}
if s == nil {
t.Error("statement is nil")
}
for i := 0; i < 1000; i++ {
ierr := s.Exec2(float64(i)*float64(3.14), i, "hello")
if ierr != nil {
t.Errorf("insert error: %s", ierr)
c := db.Changes()
if c != 1 {
t.Errorf("insert error: %d <> 1", c)
}
s.Next()
}
s.Finalize()
cs, _ := db.Prepare("SELECT COUNT(*) FROM test")
cs.Exec()
if !cs.Next() {
t.Error("no result for count")
defer cs.Finalize()
if ok, _ := cs.Next(); !ok {
t.Fatal("no result for count")
}
var i int
err := cs.Scan(&i)
if err != nil {
t.Errorf("error scanning count: %s", err)
t.Fatalf("error scanning count: %s", err)
}
if i != 1000 {
t.Errorf("count should be 1000, but it is %d", i)
}
rs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test ORDER BY int_num LIMIT 10")
rs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test ORDER BY int_num LIMIT 2")
var fnum float64
var inum int64
var sstr string
for rs.Next() {
rs.Scan(&fnum, &inum, &sstr)
fmt.Printf("fnum = %f, inum = %d, sstre = %s\n", fnum, inum, sstr)
}
if ok, _ := rs.Next(); ok {
rs.Scan(&fnum, &inum, &sstr)
if fnum != 0 {
t.Errorf("Expected 0 <> %f\n", fnum)
}
if inum != 0 {
t.Errorf("Expected 0 <> %d\n", inum)
}
if sstr != "hello" {
t.Errorf("Expected 'hello' <> %s\n", sstr)
}
}
if ok, _ := rs.Next(); ok {
var fnum float64
var inum int64
var sstr string
rs.NamedScan("a_string", &sstr, "float_num", &fnum, "int_num", &inum)
if fnum != 3.14 {
t.Errorf("Expected 3.14 <> %f\n", fnum)
}
if inum != 1 {
t.Errorf("Expected 1 <> %d\n", inum)
}
if sstr != "hello" {
t.Errorf("Expected 'hello' <> %s\n", sstr)
}
}
}
func BenchmarkScan(b *testing.B) {
b.StopTimer()
db, _ := Open("/tmp/test_bs.db")
db.Exec("DROP TABLE test")
db, _ := Open("")
defer db.Close()
db.Exec("DROP TABLE IF EXISTS test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
s, _ := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
for i := 0; i < 1000; i++ {
s.Exec(float64(i)*float64(3.14), i, "hello")
s.Next()
}
s.Finalize()
b.StartTimer()
for i := 0; i < b.N; i++ {
cs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test")
cs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test")
cs.Exec()
var fnum float64
var inum int64
var sstr string
var fnum float64
var inum int64
var sstr string
for cs.Next() {
var ok bool
var err os.Error
for ok, err = cs.Next(); ok; ok, err = cs.Next() {
cs.Scan(&fnum, &inum, &sstr)
}
if err != nil {
panic(err)
}
cs.Finalize()
}
}
func BenchmarkScan2(b *testing.B) {
func BenchmarkNamedScan(b *testing.B) {
b.StopTimer()
db, _ := Open("/tmp/test_bs2.db")
db.Exec("DROP TABLE test")
db, _ := Open("")
defer db.Close()
db.Exec("DROP TABLE IF EXISTS test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
s, _ := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
for i := 0; i < 1000; i++ {
s.Exec(float64(i)*float64(3.14), i, "hello")
s.Next()
}
s.Finalize()
b.StartTimer()
for i := 0; i < b.N; i++ {
cs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test")
cs.Exec()
cs, _ := db.Prepare("SELECT float_num, int_num, a_string FROM test")
var fnum float64
var inum int64
var sstr string
var fnum float64
var inum int64
var sstr string
for cs.Next() {
cs.Scan2(&fnum, &inum, &sstr)
var ok bool
var err os.Error
for ok, err = cs.Next(); ok; ok, err = cs.Next() {
cs.NamedScan("float_num", &fnum, "int_num", &inum, "a_string", &sstr)
}
if err != nil {
panic(err)
}
cs.Finalize()
}
}
func BenchmarkInsert(b *testing.B) {
db, _ := Open("/tmp/test_bi.db")
db.Exec("DROP TABLE test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
s, _ := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
// for x := 0; x < b.N; x++ {
for i := 0; i < b.N; i++ {
s.Exec(float64(i)*float64(3.14), i, "hello")
s.Next()
}
// }
s.Finalize()
}
func BenchmarkInsert2(b *testing.B) {
db, _ := Open("/tmp/test_bi2.db")
db.Exec("DROP TABLE test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)")
s, _ := db.Prepare("INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)")
db, _ := Open("")
defer db.Close()
db.Exec("DROP TABLE IF EXISTS test")
db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT," +
" float_num REAL, int_num INTEGER, a_string TEXT)")
s, _ := db.Prepare("INSERT INTO test (float_num, int_num, a_string)" +
" VALUES (?, ?, ?)")
defer s.Finalize()
// for x := 0; x < b.N; x++ {
for i := 0; i < b.N; i++ {
s.Exec2(float64(i)*float64(3.14), i, "hello")
s.Next()
}
// }
s.Finalize()
for i := 0; i < b.N; i++ {
s.Exec(float64(i)*float64(3.14), i, "hello")
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment