Provide more detailed error messages

Use the sqlite3_errmsg() API to retrieve more specific error
messages.

eg. Attempting to exec 'CREATE TABLE ExistingTableName (...)'
will now report 'table already exists: ExistingTableName' rather
than 'SQL logic error or missing database'
systemlib
Robert Knight 2013-11-19 09:13:19 +00:00
parent 056b49918a
commit 19cb26da92
3 changed files with 35 additions and 15 deletions

View File

@ -4,6 +4,12 @@ import "C"
type ErrNo int type ErrNo int
type Error struct {
Code ErrNo /* The error code returned by SQLite */
err string /* The error string returned by sqlite3_errmsg(),
this usually contains more specific details. */
}
// result codes from http://www.sqlite.org/c3ref/c_abort.html // result codes from http://www.sqlite.org/c3ref/c_abort.html
var ( var (
ErrError error = ErrNo(1) /* SQL error or missing database */ ErrError error = ErrNo(1) /* SQL error or missing database */
@ -37,5 +43,13 @@ var (
) )
func (err ErrNo) Error() string { func (err ErrNo) Error() string {
return errorString(err) return Error{Code: err}.Error()
}
func (err Error) Error() string {
if err.err != "" {
return err.err
} else {
return errorString(err)
}
} }

View File

@ -27,7 +27,7 @@ func TestFailures(t *testing.T) {
if err == nil { if err == nil {
_, err = db.Exec("drop table foo") _, err = db.Exec("drop table foo")
} }
if err != ErrNotADB { if err.Code != ErrNotADB {
t.Error("wrong error code for corrupted DB") t.Error("wrong error code for corrupted DB")
} }
if err.Error() == "" { if err.Error() == "" {

View File

@ -138,6 +138,12 @@ func (c *SQLiteConn) AutoCommit() bool {
return int(C.sqlite3_get_autocommit(c.db)) != 0 return int(C.sqlite3_get_autocommit(c.db)) != 0
} }
func (c *SQLiteConn) lastError() Error {
return Error{Code: ErrNo(C.sqlite3_errcode(c.db)),
err: C.GoString(C.sqlite3_errmsg(c.db)),
}
}
// TODO: Execer & Queryer currently disabled // TODO: Execer & Queryer currently disabled
// https://github.com/mattn/go-sqlite3/issues/82 // https://github.com/mattn/go-sqlite3/issues/82
//// Implements Execer //// Implements Execer
@ -205,7 +211,7 @@ func (c *SQLiteConn) exec(cmd string) error {
defer C.free(unsafe.Pointer(pcmd)) defer C.free(unsafe.Pointer(pcmd))
rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil) rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return c.lastError()
} }
return nil return nil
} }
@ -218,8 +224,8 @@ func (c *SQLiteConn) Begin() (driver.Tx, error) {
return &SQLiteTx{c}, nil return &SQLiteTx{c}, nil
} }
func errorString(err ErrNo) string { func errorString(err Error) string {
return C.GoString(C.sqlite3_errstr(C.int(err))) return C.GoString(C.sqlite3_errstr(C.int(err.Code)))
} }
// Open database and return a new connection. // Open database and return a new connection.
@ -242,7 +248,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
C.SQLITE_OPEN_CREATE, C.SQLITE_OPEN_CREATE,
nil) nil)
if rv != 0 { if rv != 0 {
return nil, ErrNo(rv) return nil, Error{Code: ErrNo(rv)}
} }
if db == nil { if db == nil {
return nil, errors.New("sqlite succeeded without returning a database") return nil, errors.New("sqlite succeeded without returning a database")
@ -250,7 +256,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
rv = C.sqlite3_busy_timeout(db, 5000) rv = C.sqlite3_busy_timeout(db, 5000)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return nil, ErrNo(rv) return nil, Error{Code: ErrNo(rv)}
} }
conn := &SQLiteConn{db} conn := &SQLiteConn{db}
@ -295,7 +301,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
func (c *SQLiteConn) Close() error { func (c *SQLiteConn) Close() error {
rv := C.sqlite3_close_v2(c.db) rv := C.sqlite3_close_v2(c.db)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return c.lastError()
} }
c.db = nil c.db = nil
return nil return nil
@ -309,7 +315,7 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
var tail *C.char var tail *C.char
rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail) rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return nil, ErrNo(rv) return nil, c.lastError()
} }
var t string var t string
if tail != nil && C.strlen(tail) > 0 { if tail != nil && C.strlen(tail) > 0 {
@ -329,7 +335,7 @@ func (s *SQLiteStmt) Close() error {
} }
rv := C.sqlite3_finalize(s.s) rv := C.sqlite3_finalize(s.s)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return s.c.lastError()
} }
return nil return nil
} }
@ -342,7 +348,7 @@ func (s *SQLiteStmt) NumInput() int {
func (s *SQLiteStmt) bind(args []driver.Value) error { func (s *SQLiteStmt) bind(args []driver.Value) error {
rv := C.sqlite3_reset(s.s) rv := C.sqlite3_reset(s.s)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
return ErrNo(rv) return s.c.lastError()
} }
for i, v := range args { for i, v := range args {
@ -387,7 +393,7 @@ func (s *SQLiteStmt) bind(args []driver.Value) error {
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
} }
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return s.c.lastError()
} }
} }
return nil return nil
@ -418,7 +424,7 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
} }
rv := C.sqlite3_step(s.s) rv := C.sqlite3_step(s.s)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
return nil, ErrNo(rv) return nil, s.c.lastError()
} }
res := &SQLiteResult{ res := &SQLiteResult{
@ -435,7 +441,7 @@ func (rc *SQLiteRows) Close() error {
} }
rv := C.sqlite3_reset(rc.s.s) rv := C.sqlite3_reset(rc.s.s)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return rc.s.c.lastError()
} }
return nil return nil
} }
@ -460,7 +466,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
if rv != C.SQLITE_ROW { if rv != C.SQLITE_ROW {
rv = C.sqlite3_reset(rc.s.s) rv = C.sqlite3_reset(rc.s.s)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return ErrNo(rv) return rc.s.c.lastError()
} }
return nil return nil
} }