diff --git a/error.go b/error.go index 15c843e..7568505 100644 --- a/error.go +++ b/error.go @@ -4,6 +4,12 @@ import "C" 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 var ( ErrError error = ErrNo(1) /* SQL error or missing database */ @@ -37,5 +43,13 @@ var ( ) 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) + } } diff --git a/error_test.go b/error_test.go index afa7b27..bf7bb3c 100644 --- a/error_test.go +++ b/error_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func TestFailures(t *testing.T) { +func TestCorruptDbErrors(t *testing.T) { dirName, err := ioutil.TempDir("", "sqlite3") if err != nil { t.Fatal(err) @@ -27,7 +27,9 @@ func TestFailures(t *testing.T) { if err == nil { _, err = db.Exec("drop table foo") } - if err != ErrNotADB { + + sqliteErr := err.(Error) + if sqliteErr.Code != ErrNotADB { t.Error("wrong error code for corrupted DB") } if err.Error() == "" { @@ -35,3 +37,28 @@ func TestFailures(t *testing.T) { } db.Close() } + +func TestSqlLogicErrors(t *testing.T) { + dirName, err := ioutil.TempDir("", "sqlite3") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dirName) + + dbFileName := path.Join(dirName, "test.db") + db, err := sql.Open("sqlite3", dbFileName) + if err != nil { + t.Error(err) + } + + _, err = db.Exec("CREATE TABLE Foo (id INT PRIMARY KEY)") + if err != nil { + t.Error(err) + } + + const expectedErr = "table Foo already exists" + _, err = db.Exec("CREATE TABLE Foo (id INT PRIMARY KEY)") + if err.Error() != expectedErr { + t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr) + } +} diff --git a/sqlite3.go b/sqlite3.go index eef4b1f..d2c566a 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -138,6 +138,12 @@ func (c *SQLiteConn) AutoCommit() bool { 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 // https://github.com/mattn/go-sqlite3/issues/82 //// Implements Execer @@ -205,7 +211,7 @@ func (c *SQLiteConn) exec(cmd string) error { defer C.free(unsafe.Pointer(pcmd)) rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil) if rv != C.SQLITE_OK { - return ErrNo(rv) + return c.lastError() } return nil } @@ -218,8 +224,8 @@ func (c *SQLiteConn) Begin() (driver.Tx, error) { return &SQLiteTx{c}, nil } -func errorString(err ErrNo) string { - return C.GoString(C.sqlite3_errstr(C.int(err))) +func errorString(err Error) string { + return C.GoString(C.sqlite3_errstr(C.int(err.Code))) } // Open database and return a new connection. @@ -242,7 +248,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { C.SQLITE_OPEN_CREATE, nil) if rv != 0 { - return nil, ErrNo(rv) + return nil, Error{Code: ErrNo(rv)} } if db == nil { 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) if rv != C.SQLITE_OK { - return nil, ErrNo(rv) + return nil, Error{Code: ErrNo(rv)} } conn := &SQLiteConn{db} @@ -295,7 +301,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { func (c *SQLiteConn) Close() error { rv := C.sqlite3_close_v2(c.db) if rv != C.SQLITE_OK { - return ErrNo(rv) + return c.lastError() } c.db = nil return nil @@ -309,7 +315,7 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { var tail *C.char rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &tail) if rv != C.SQLITE_OK { - return nil, ErrNo(rv) + return nil, c.lastError() } var t string if tail != nil && C.strlen(tail) > 0 { @@ -329,7 +335,7 @@ func (s *SQLiteStmt) Close() error { } rv := C.sqlite3_finalize(s.s) if rv != C.SQLITE_OK { - return ErrNo(rv) + return s.c.lastError() } return nil } @@ -342,7 +348,7 @@ func (s *SQLiteStmt) NumInput() int { func (s *SQLiteStmt) bind(args []driver.Value) error { rv := C.sqlite3_reset(s.s) 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 { @@ -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))) } if rv != C.SQLITE_OK { - return ErrNo(rv) + return s.c.lastError() } } return nil @@ -418,7 +424,7 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { } rv := C.sqlite3_step(s.s) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { - return nil, ErrNo(rv) + return nil, s.c.lastError() } res := &SQLiteResult{ @@ -435,7 +441,7 @@ func (rc *SQLiteRows) Close() error { } rv := C.sqlite3_reset(rc.s.s) if rv != C.SQLITE_OK { - return ErrNo(rv) + return rc.s.c.lastError() } return nil } @@ -460,7 +466,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { if rv != C.SQLITE_ROW { rv = C.sqlite3_reset(rc.s.s) if rv != C.SQLITE_OK { - return ErrNo(rv) + return rc.s.c.lastError() } return nil }