From ff8e6729ce0628c3da97bd8e85c636f3645c2516 Mon Sep 17 00:00:00 2001 From: Jochen Voss <voss@seehuhn.de> Date: Thu, 20 Jun 2013 21:52:38 +0100 Subject: [PATCH] Start work on introducing machine-readable error codes. This commit introduces a new type 'ErrNo', implementing the error interface. Constants for all sqlite3 error codes are provided in the new source file "error.go". --- error.go | 41 +++++++++++++++++++++++++++++++++++++++++ error_test.go | 34 ++++++++++++++++++++++++++++++++++ sqlite3.go | 26 +++++++++++++++----------- sqlite3_other.go | 2 +- sqlite3_test.go | 2 +- sqlite3_windows.go | 2 +- 6 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 error.go create mode 100644 error_test.go diff --git a/error.go b/error.go new file mode 100644 index 0000000..15c843e --- /dev/null +++ b/error.go @@ -0,0 +1,41 @@ +package sqlite3 + +import "C" + +type ErrNo int + +// result codes from http://www.sqlite.org/c3ref/c_abort.html +var ( + ErrError error = ErrNo(1) /* SQL error or missing database */ + ErrInternal error = ErrNo(2) /* Internal logic error in SQLite */ + ErrPerm error = ErrNo(3) /* Access permission denied */ + ErrAbort error = ErrNo(4) /* Callback routine requested an abort */ + ErrBusy error = ErrNo(5) /* The database file is locked */ + ErrLocked error = ErrNo(6) /* A table in the database is locked */ + ErrNomem error = ErrNo(7) /* A malloc() failed */ + ErrReadonly error = ErrNo(8) /* Attempt to write a readonly database */ + ErrInterrupt error = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ + ErrIoErr error = ErrNo(10) /* Some kind of disk I/O error occurred */ + ErrCorrupt error = ErrNo(11) /* The database disk image is malformed */ + ErrNotFound error = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ + ErrFull error = ErrNo(13) /* Insertion failed because database is full */ + ErrCantOpen error = ErrNo(14) /* Unable to open the database file */ + ErrProtocol error = ErrNo(15) /* Database lock protocol error */ + ErrEmpty error = ErrNo(16) /* Database is empty */ + ErrSchema error = ErrNo(17) /* The database schema changed */ + ErrTooBig error = ErrNo(18) /* String or BLOB exceeds size limit */ + ErrConstraint error = ErrNo(19) /* Abort due to constraint violation */ + ErrMismatch error = ErrNo(20) /* Data type mismatch */ + ErrMisuse error = ErrNo(21) /* Library used incorrectly */ + ErrNoLFS error = ErrNo(22) /* Uses OS features not supported on host */ + ErrAuth error = ErrNo(23) /* Authorization denied */ + ErrFormat error = ErrNo(24) /* Auxiliary database format error */ + ErrRange error = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ + ErrNotADB error = ErrNo(26) /* File opened that is not a database file */ + ErrNotice error = ErrNo(27) /* Notifications from sqlite3_log() */ + ErrWarning error = ErrNo(28) /* Warnings from sqlite3_log() */ +) + +func (err ErrNo) Error() string { + return errorString(err) +} diff --git a/error_test.go b/error_test.go new file mode 100644 index 0000000..197b2f0 --- /dev/null +++ b/error_test.go @@ -0,0 +1,34 @@ +package sqlite3 + +import ( + "database/sql" + "io/ioutil" + "os" + "path" + "testing" +) + +func TestFailures(t *testing.T) { + dirName, err := ioutil.TempDir("", "sqlite3") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dirName) + + dbFileName := path.Join(dirName, "test.db") + f, err := os.Create(dbFileName) + if err != nil { + t.Error(err) + } + f.Write([]byte{1, 2, 3, 4, 5}) + f.Close() + + db, err := sql.Open("sqlite3", dbFileName) + if err == nil { + _, err = db.Exec("drop table foo") + } + if err != ErrNotADB { + t.Error("wrong error code for corrupted DB") + } + db.Close() +} diff --git a/sqlite3.go b/sqlite3.go index 11bfba4..4e390e4 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -132,7 +132,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 errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + return ErrNo(rv) } return nil } @@ -145,6 +145,10 @@ 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))) +} + // Open database and return a new connection. // You can specify DSN string with URI filename. // test.db @@ -165,7 +169,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { C.SQLITE_OPEN_CREATE, nil) if rv != 0 { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) + return nil, ErrNo(rv) } if db == nil { return nil, errors.New("sqlite succeeded without returning a database") @@ -173,7 +177,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { rv = C.sqlite3_busy_timeout(db, 5000) if rv != C.SQLITE_OK { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) + return nil, ErrNo(rv) } return &SQLiteConn{db}, nil @@ -188,7 +192,7 @@ func (c *SQLiteConn) Close() error { } rv := C.sqlite3_close(c.db) if rv != C.SQLITE_OK { - return errors.New("error while closing sqlite database connection") + return ErrNo(rv) } c.db = nil return nil @@ -202,7 +206,7 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { var perror *C.char rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &perror) if rv != C.SQLITE_OK { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(c.db))) + return nil, ErrNo(rv) } var t string if perror != nil && C.strlen(perror) > 0 { @@ -222,7 +226,7 @@ func (s *SQLiteStmt) Close() error { } rv := C.sqlite3_finalize(s.s) if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) + return ErrNo(rv) } return nil } @@ -235,7 +239,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 errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) + return ErrNo(rv) } for i, v := range args { @@ -280,7 +284,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 errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) + return ErrNo(rv) } } return nil @@ -311,7 +315,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, errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) + return nil, ErrNo(rv) } res := &SQLiteResult{ @@ -325,7 +329,7 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { func (rc *SQLiteRows) Close() error { rv := C.sqlite3_reset(rc.s.s) if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db))) + return ErrNo(rv) } return nil } @@ -348,7 +352,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { return io.EOF } if rv != C.SQLITE_ROW { - return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db))) + return ErrNo(rv) } if rc.decltype == nil { diff --git a/sqlite3_other.go b/sqlite3_other.go index 58030db..96fb194 100644 --- a/sqlite3_other.go +++ b/sqlite3_other.go @@ -1,6 +1,6 @@ // +build !windows -package sqlite +package sqlite3 /* #cgo pkg-config: sqlite3 diff --git a/sqlite3_test.go b/sqlite3_test.go index be337ae..f614cef 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -1,4 +1,4 @@ -package sqlite +package sqlite3 import ( "crypto/rand" diff --git a/sqlite3_windows.go b/sqlite3_windows.go index 322400f..7193dfb 100644 --- a/sqlite3_windows.go +++ b/sqlite3_windows.go @@ -1,4 +1,4 @@ -package sqlite +package sqlite3 /* #cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe