use exn:fail:sql for sqlite errors too

This commit is contained in:
Ryan Culpepper 2012-11-30 18:19:48 -05:00
parent 05e7e61d85
commit 30397acc37
3 changed files with 66 additions and 45 deletions

View File

@ -158,6 +158,8 @@ Only errors with an associated SQLSTATE are represented by
exn:fail:sql, specifically only errors originating from a database
backend or library. Other errors are typically raised using 'error',
producing plain old exn:fail.
For SQLite, use symbol instead of SQLSTATE string.
|#
;; exn:fail:sql

View File

@ -351,46 +351,54 @@
;; Returns the status code if no error occurred, otherwise
;; raises an exception with an appropriate message.
(define (handle-status* who s db)
(if (or (= s SQLITE_OK)
(= s SQLITE_ROW)
(= s SQLITE_DONE))
s
(error who "~a" (lookup-status-message s db))))
(cond [(or (= s SQLITE_OK)
(= s SQLITE_ROW)
(= s SQLITE_DONE))
s]
[else
(let* ([info
(or (assoc s error-table)
'(#f unknown "unknown error code"))]
[sym
(cadr info)]
[message
(cond [(and (= s SQLITE_ERROR) db)
(sqlite3_errmsg db)]
[else (caddr info)])])
(raise (make-exn:fail:sql (format "~a: ~a" who message)
(current-continuation-marks)
sym
`((code . ,sym)
(message . ,message)
(errcode . ,s)))))]))
(define error-table
`([,SQLITE_ERROR . "unknown error"]
[,SQLITE_INTERNAL . "an internal logic error in SQLite"]
[,SQLITE_PERM . "access permission denied"]
[,SQLITE_ABORT . "callback routine requested an abort"]
[,SQLITE_BUSY . "the database file is locked"]
[,SQLITE_LOCKED . "table in the database is locked"]
[,SQLITE_NOMEM . "malloc() failed"]
[,SQLITE_READONLY . "attempt to write a readonly database"]
[,SQLITE_INTERRUPT . "operation terminated by sqlite3_interrupt()"]
[,SQLITE_IOERR . "some kind of disk I/O error occurred"]
[,SQLITE_CORRUPT . "the database disk image is malformed"]
[,SQLITE_NOTFOUND . "(internal only) table or record not found"]
[,SQLITE_FULL . "insertion failed because database is full"]
[,SQLITE_CANTOPEN . "unable to open the database file"]
[,SQLITE_PROTOCOL . "database lock protocol error"]
[,SQLITE_EMPTY . "database is empty"]
[,SQLITE_SCHEMA . "database schema changed"]
[,SQLITE_TOOBIG . "too much data for one row of a table"]
[,SQLITE_CONSTRAINT . "abort due to constraint violation"]
[,SQLITE_MISMATCH . "data type mismatch"]
[,SQLITE_MISUSE . "library used incorrectly"]
[,SQLITE_NOLFS . "uses OS features not supported on host"]
[,SQLITE_AUTH . "authorization denied"]
[,SQLITE_FORMAT . "auxiliary database format error"]
[,SQLITE_RANGE . "2nd parameter to sqlite3_bind out of range"]
[,SQLITE_NOTADB . "file opened that is not a database file"]))
;; lookup-status-message : integer db/#f -> string
(define (lookup-status-message s db)
(cond [(and (eq? s SQLITE_ERROR) db)
(sqlite3_errmsg db)]
[(assoc s error-table) => cdr]
[else "unknown condition"]))
`([,SQLITE_ERROR error "unknown error"]
[,SQLITE_INTERNAL internal "an internal logic error in SQLite"]
[,SQLITE_PERM perm "access permission denied"]
[,SQLITE_ABORT abort "callback routine requested an abort"]
[,SQLITE_BUSY busy "the database file is locked"]
[,SQLITE_LOCKED locked "table in the database is locked"]
[,SQLITE_NOMEM nomem "malloc() failed"]
[,SQLITE_READONLY readonly "attempt to write a readonly database"]
[,SQLITE_INTERRUPT interrupt "operation terminated by sqlite3_interrupt()"]
[,SQLITE_IOERR ioerr "some kind of disk I/O error occurred"]
[,SQLITE_CORRUPT corrupt "the database disk image is malformed"]
[,SQLITE_NOTFOUND notfound "(internal only) table or record not found"]
[,SQLITE_FULL full "insertion failed because database is full"]
[,SQLITE_CANTOPEN cantopen "unable to open the database file"]
[,SQLITE_PROTOCOL protocol "database lock protocol error"]
[,SQLITE_EMPTY empty "database is empty"]
[,SQLITE_SCHEMA schema "database schema changed"]
[,SQLITE_TOOBIG toobig "too much data for one row of a table"]
[,SQLITE_CONSTRAINT constraint "abort due to constraint violation"]
[,SQLITE_MISMATCH mismatch "data type mismatch"]
[,SQLITE_MISUSE misuse "library used incorrectly"]
[,SQLITE_NOLFS nolfs "uses OS features not supported on host"]
[,SQLITE_AUTH auth "authorization denied"]
[,SQLITE_FORMAT format "auxiliary database format error"]
[,SQLITE_RANGE range "2nd parameter to sqlite3_bind out of range"]
[,SQLITE_NOTADB notadb "file opened that is not a database file"]))
;; http://www.sqlite.org/lang_transaction.html
(define maybe-rollback-status-list

View File

@ -740,14 +740,27 @@ SQL errors are represented by the @racket[exn:fail:sql] exception
type.
@defstruct[(exn:fail:sql exn:fail)
([sqlstate string?]
([sqlstate (or/c string? symbol?)]
[info (listof (cons/c symbol? any/c))])]{
Represents a SQL error originating from the database server or
native library. The @racket[sqlstate] field contains the SQLSTATE
code (a five-character string) of the error; refer to the database
system's documentation for the definitions of SQLSTATE codes. The
@racket[info] field contains all information available about the
code (a five-character string) of the error for PostgreSQL, MySQL,
or ODBC connections or a symbol for SQLite connections. Refer to the
database system's documentation for the definitions of error codes:
@itemlist[
@item{@hyperlink["http://www.postgresql.org/docs/9.0/static/errcodes-appendix.html"]{
PostgreSQL SQLSTATE codes}}
@item{@hyperlink["http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html"]{
MySQL SQLSTATE codes}}
@item{@hyperlink["http://www.sqlite.org/c3ref/c_abort.html"]{
SQLite error codes}; errors are represented as a symbol based on
the error constant's name, such as @racket['busy] for @tt{SQLITE_BUSY}}
@item{ODBC: see the database system's documentation}
]
The @racket[info] field contains all information available about the
error as an association list. The available keys vary, but the
@racket['message] key is typically present; its value is a string
containing the error message.
@ -759,9 +772,7 @@ type.
Errors originating from the @racketmodname[db] library, such as
arity and contract errors, type conversion errors, etc, are not
represented by @racket[exn:fail:sql]. SQLite errors are not
represented via @racket[exn:fail:sql], because SQLite does not
provide SQLSTATE error codes.
represented by @racket[exn:fail:sql].
}
@section{Database Catalog Information}