branch: externals/pq commit 499dc3b50e5963c939b20cab063855a0f7afb969 Author: Andreas Seltenreich <seltenre...@gmx.de> Commit: Andreas Seltenreich <seltenre...@gmx.de>
Re-introduce custom error signal pq:error with SQLSTATE. --- README.org | 15 ++++++++++++++- pq.c | 26 ++++++++++++++++++++++---- test.el | 13 ++++++++++++- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.org b/README.org index cf3433bb72..6a14d519de 100644 --- a/README.org +++ b/README.org @@ -29,7 +29,20 @@ Basic usage: : ["font-lock-major-mode" 24] : ["font-lock-mode-major-mode" 24]) -See the testsuite [[./test.el]] for all implemented features. + +=pq= raises SQL errors as error signal =pq:error=. This provides the +[[https://www.postgresql.org/docs/current/errcodes-appendix.html][SQLSTATE]] error code in an additional string in the error data list. +For example, you can reliably catch unique violations like this: + +: (condition-case err (pq:query *pq* "insert into t values (666)") +: (pq:error +: (if (string= "23505" (nth 2 err)) +: (progn +: (message "Caught a unqiue violation")) +: ;; re-throw anything else +: (signal (car err) (cdr err))))) + +See the testsuite [[./test.el]] for more implemented features. Note that =pq= silently converts bigints and numerics your queries return to lisp floats because they don't fit into a lisp integer. diff --git a/pq.c b/pq.c index 7b1cefd18d..ecff3bfd45 100644 --- a/pq.c +++ b/pq.c @@ -51,12 +51,18 @@ static bool result_ok(emacs_env *env, PGresult *res) default: { const char *errmsg = PQresultErrorMessage(res); - emacs_value errstring = env->make_string(env, errmsg, strlen(errmsg)); - emacs_value Qpq_error = env->intern (env, "error"); - emacs_value errdata = env->funcall(env, env->intern(env, "list"), 1, &errstring); + const char *sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); + emacs_value Qpq_error = env->intern (env, "pq:error"); + emacs_value errmsg_string = + env->make_string(env, errmsg, strlen(errmsg)); + emacs_value sqlstate_string = + env->make_string(env, sqlstate, strlen(sqlstate)); + emacs_value errdata[] = {errmsg_string, sqlstate_string}; + emacs_value errdata_list = + env->funcall(env, env->intern(env, "list"), 2, errdata); PQclear(res); - env->non_local_exit_signal(env, Qpq_error, errdata); + env->non_local_exit_signal(env, Qpq_error, errdata_list); } return false; } @@ -386,6 +392,18 @@ emacs_module_init (struct emacs_runtime *ert) #undef DEFUN + /* Define custom error signal. The error data is a list with two + * strings. The first string is the human-readable message, the + * second is the SQLSTATE error code. */ + { + emacs_value Fdefine_error = env->intern (env, "define-error"); + emacs_value Qpq_error = env->intern (env, "pq:error"); + emacs_value errmsg_string = + env->make_string(env, "SQL error", strlen("SQL error")); + emacs_value args[] = {Qpq_error, errmsg_string}; + env->funcall(env, Fdefine_error, 2, args); + } + provide(env, "pq"); /* loaded successfully */ diff --git a/test.el b/test.el index 956f9bb4c5..68d84a2735 100644 --- a/test.el +++ b/test.el @@ -73,7 +73,18 @@ (pq:query conn "select 1") (should-error (pq:query "select * from")) (should-error (pq:query conn "select * from")) - (should-error (pq:query conn "select $1::text")))) + (should-error (pq:query conn "select $1::text")) + (should + (equal + 'ok + (condition-case err + (pq:query conn "moo") + (pq:error + (if (string= "42601" (nth 2 err)) + ;; syntax errors are ok + 'ok + (signal (car err) (cdr err))))))) +)) (ert-deftest pq-reset-connection-test () (let ((testconn (pq:connectdb *conninfo*))