Skip to content

Commit

Permalink
Fix for issue #11 along with tidying up of error status handling
Browse files Browse the repository at this point in the history
  • Loading branch information
feyeleanor committed Feb 18, 2012
1 parent 5174223 commit d6cf7d5
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 76 deletions.
31 changes: 20 additions & 11 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ var errText = map[Errno]string{
SAVEPOINT: "invalid or unknown savepoint identifier",
}

func SQLiteError(code C.int) (e error) {
if e = Errno(code); e == OK {
e = nil
}
return
}

// Database implements high level view of the underlying database.
type Database struct {
handle *C.sqlite3
Expand Down Expand Up @@ -127,9 +134,15 @@ func (db *Database) Open(flags ...int) (e error) {
for _, v := range flags {
db.Flags = db.Flags | C.int(v)
}
if err := Errno(C.sqlite3_open_v2(C.CString(db.Filename), &db.handle, db.Flags, nil)); err != OK {
e = err
} else if db.handle == nil {

e = SQLiteError(C.sqlite3_open_v2(C.CString(db.Filename), &db.handle, db.Flags, nil))

// if err :; err != OK {
// e = err
// } else if db.handle == nil {
// e = CANTOPEN
// }
if e == nil && db.handle == nil {
e = CANTOPEN
}
}
Expand Down Expand Up @@ -191,8 +204,8 @@ func (db *Database) Error() error {
// supplied values.
func (db *Database) Prepare(sql string, values ...interface{}) (s *Statement, e error) {
s = &Statement{db: db, timestamp: time.Now().UnixNano()}
if rv := Errno(C.sqlite3_prepare_v2(db.handle, C.CString(sql), -1, &s.cptr, nil)); rv != OK {
s, e = nil, rv
if e = SQLiteError(C.sqlite3_prepare_v2(db.handle, C.CString(sql), -1, &s.cptr, nil)); e != nil {
s = nil
} else {
if len(values) > 0 {
e, _ = s.BindAll(values...)
Expand All @@ -204,13 +217,9 @@ func (db *Database) Prepare(sql string, values ...interface{}) (s *Statement, e
// Execute runs the SQL statement.
func (db *Database) Execute(sql string, f ...func(*Statement, ...interface{})) (c int, e error) {
var st *Statement
st, e = db.Prepare(sql)
if e == nil {
if st, e = db.Prepare(sql); e == nil {
c, e = st.All(f...)
}
if e == OK {
e = nil
}
return
}

Expand Down Expand Up @@ -353,7 +362,7 @@ func (db *Database) Backup(p BackupParameters) (r Reporter, e error) {
Remaining: backup.Remaining(),
}
r <- report
if e, ok := report.Error.(Errno); ok && !(e == OK || e == BUSY || e == LOCKED) {
if e := report.Error; !(e == nil || e == BUSY || e == LOCKED) {
break
}
if p.Interval > 0 {
Expand Down
22 changes: 22 additions & 0 deletions database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,26 @@ func TestBackup(t *testing.T) {
t.Logf("backup of %v generated %v asynchronous messages and took %vns with interval %v", db.Filename, messages, time.Now().UnixNano() - d, 100000)
}
})
}

func TestExecute(t *testing.T) {
t.Log("Test case for issue #11")
db := TransientDatabase()
// OPEN_FULLMUTEX, OPEN_READWRITE, OPEN_CREATE
if e := db.Open(0x10000, 0x02, 0x04); e != nil {
t.Logf("Open failed: %v", e)
}
defer db.Close()
if _, e := db.Execute("CREATE TABLE t(id INTEGER PRIMARY KEY ASC, unique_int INTEGER UNIQUE);"); e != nil {
t.Logf("Create table failed: %v", e)
}
if _, e := db.Execute("INSERT INTO t ( unique_int ) VALUES ( 1 );"); e != nil {
t.Logf("1. Insert failed: %v", e)
}
if _, e := db.Execute("INSERT INTO t ( unique_int ) VALUES ( 2 );"); e != nil {
t.Logf("2. Insert failed: %v", e)
}
if _, e := db.Execute("INSERT INTO t ( unique_int ) VALUES ( 2 );"); e == nil {
t.Logf("3. Insert succeeded: %v", e)
}
}
25 changes: 9 additions & 16 deletions query_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ import (

type QueryParameter int
func (p QueryParameter) bind_blob(s *Statement, v []byte) error {
if e := Errno(C.gosqlite3_bind_blob(s.cptr, C.int(p), unsafe.Pointer(C.CString(string(v))), C.int(len(v)))); e != OK {
return e
}
return nil
return SQLiteError(C.gosqlite3_bind_blob(s.cptr, C.int(p), unsafe.Pointer(C.CString(string(v))), C.int(len(v))))
}

// Bind replaces the literals placed in the SQL statement with the actual
Expand All @@ -34,32 +31,28 @@ func (p QueryParameter) bind_blob(s *Statement, v []byte) error {
// In the templates above, NNN represents an integer literal, VVV represents
// an alphanumeric identifier.
func (p QueryParameter) Bind(s *Statement, value interface{}) (e error) {
var rv Errno
switch v := value.(type) {
case nil:
rv = Errno(C.sqlite3_bind_null(s.cptr, C.int(p)))
e = SQLiteError(C.sqlite3_bind_null(s.cptr, C.int(p)))
case int:
rv = Errno(C.sqlite3_bind_int(s.cptr, C.int(p), C.int(v)))
e = SQLiteError(C.sqlite3_bind_int(s.cptr, C.int(p), C.int(v)))
case string:
rv = Errno(C.gosqlite3_bind_text(s.cptr, C.int(p), C.CString(v), C.int(len(v))))
e = SQLiteError(C.gosqlite3_bind_text(s.cptr, C.int(p), C.CString(v), C.int(len(v))))
case int64:
rv = Errno(C.sqlite3_bind_int64(s.cptr, C.int(p), C.sqlite3_int64(v)))
e = SQLiteError(C.sqlite3_bind_int64(s.cptr, C.int(p), C.sqlite3_int64(v)))
case float32:
rv = Errno(C.sqlite3_bind_double(s.cptr, C.int(p), C.double(v)))
e = SQLiteError(C.sqlite3_bind_double(s.cptr, C.int(p), C.double(v)))
case float64:
rv = Errno(C.sqlite3_bind_double(s.cptr, C.int(p), C.double(v)))
e = SQLiteError(C.sqlite3_bind_double(s.cptr, C.int(p), C.double(v)))
default:
buffer := new(bytes.Buffer)
encoder := gob.NewEncoder(buffer)
if encoder.Encode(value) != nil {
rv = ENCODER
e = ENCODER
} else {
rawbuffer := string(buffer.Bytes())
rv = Errno(C.gosqlite3_bind_blob(s.cptr, C.int(p), unsafe.Pointer(C.CString(rawbuffer)), C.int(len(rawbuffer))))
e = SQLiteError(C.gosqlite3_bind_blob(s.cptr, C.int(p), unsafe.Pointer(C.CString(rawbuffer)), C.int(len(rawbuffer))))
}
}
if rv != OK {
e = rv
}
return
}
62 changes: 13 additions & 49 deletions statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,62 +78,32 @@ func (s *Statement) SQLSource() (sql string) {
}

// Finalize is used to delete a prepared statement in the SQLite engine.
func (s *Statement) Finalize() error {
if e := Errno(C.sqlite3_finalize(s.cptr)); e != OK {
return e
}
return nil
func (s *Statement) Finalize() (e error) {
return SQLiteError(C.sqlite3_finalize(s.cptr))
}

// Step must be called one or more times to evaluate the statement after the
// prepared statement has been prepared.
func (s *Statement) Step(f... func(*Statement, ...interface{})) (e error) {
r := Errno(C.sqlite3_step(s.cptr))
switch r {
case DONE:
e = nil
switch e = SQLiteError(C.sqlite3_step(s.cptr)); e {
case ROW:
if f != nil {
defer func() {
switch x := recover().(type) {
case nil: e = ROW
case error: e = x
default: e = MISUSE
}
}()
r := s.Row()
for _, fn := range f {
fn(s, r...)
}
row := s.Row()
for _, fn := range f {
fn(s, row...)
}
default:
e = r
case DONE:
e = s.Reset()
}
return
}

// All can be used to return all rows of a prepared statement after the
// statement has been prepared.
func (s *Statement) All(f... func(*Statement, ...interface{})) (c int, e error) {
for {
if e = s.Step(f...); e != nil {
if r, ok := e.(Errno); ok {
switch r {
case ROW:
c++
continue
default:
e = r
break
}
}
} else {
break
}
}
if e == nil {
s.Finalize()
for e = s.Step(f...); e == ROW; e = s.Step(f...) {
c++
}
e = s.Finalize()
return
}

Expand All @@ -143,16 +113,10 @@ func (s *Statement) All(f... func(*Statement, ...interface{})) (c int, e error)
// Any SQL statement variables that had values bound to them retain
// their values. Use `ClearBindings` to reset the bindings.
func (s *Statement) Reset() error {
if e := Errno(C.sqlite3_reset(s.cptr)); e != OK {
return e
}
return nil
return SQLiteError(C.sqlite3_reset(s.cptr))
}

// ClearBindings is used to reset all parameters to NULL.
func (s *Statement) ClearBindings() error {
if e := Errno(C.sqlite3_clear_bindings(s.cptr)); e != OK {
return e
}
return nil
return SQLiteError(C.sqlite3_clear_bindings(s.cptr))
}

0 comments on commit d6cf7d5

Please sign in to comment.