This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-08-13
Channels
- # babashka (3)
- # beginners (91)
- # calva (10)
- # cherry (1)
- # clj-commons (1)
- # clj-kondo (8)
- # clojure (19)
- # clojure-australia (1)
- # clojure-china (1)
- # clojure-europe (16)
- # clojure-filipino (1)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-norway (2)
- # clojure-sg (1)
- # clojure-taiwan (1)
- # cursive (12)
- # data-science (4)
- # datalevin (3)
- # emacs (16)
- # events (9)
- # hyperfiddle (1)
- # juxt (1)
- # lsp (2)
- # missionary (2)
- # music (1)
- # nbb (14)
- # off-topic (15)
- # pathom (5)
- # releases (1)
- # shadow-cljs (41)
- # sql (10)
- # squint (13)
- # vim (7)
- # xtdb (35)
Is there any way to get detail data from query errors besides parsing the error message string? From something like this:
Execution error (PSQLException) at org.postgresql.core.v3.QueryExecutorImpl/receiveErrorResponse (QueryExecutorImpl.java:2676).
ERROR: duplicate key value violates unique constraint "..."
Detail: Key (x)=(y) already exists.
`The underlying SQLException
doesn't give you much access/leverage: https://docs.oracle.com/en/java/javase/17/docs/api/java.sql/java/sql/SQLException.html
It's possible some vendor-specific exceptions under the hood might give you more convenient details but you've have to look at their docs.
In general, calling bean
(or clojure.java.data
's equivalent -- since it's a dependency of next.jdbc
) on an exception might get you some of the way.
@U04V70XH6 are there some idiomatic/common approaches to error handling with next-jdbc, given these underlying limitations? Does it come down to catching high-level exceptions and interpreting them within their context to re-throw something with more application-specific error detail?
I don't think there's much difference between error handling around
next.jdbc` and the broader picture in Clojure with exceptions, with a couple of notable specifics such as "expecting" an INSERT
to fail with a constraint violation (`SQLIntegrityConstraintViolationException`) where you might want to catch a more specific exception and retry or perform some other action.
Thanks @U04V70XH6. I'll take some time to study the class hierarchy. SQLIntegrityConstraintViolationException
is a helpful pointer.
I know this is an old thread, but I saw that you’re working with Postgres, and it turns out the Postgres JDBC driver has pretty good programmatic access to error details, just not in a very clojure-friendly way.
First, java.sql.SqlException
has getSQLState
and getErrorCode
methods. For the Postgres driver, one of those (I forget which) will contain the https://www.postgresql.org/docs/current/errcodes-appendix.html of the error. When we’ve needed to identify specific error causes e.g. to map to a domain-specific error type, we just add a mapping from the code to a more readable name in a big map in a util file.
Second, PsqlException
(the vendor-specific version of SqlException
) has a .getServerErrorMessage
method, which returns https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/util/ServerErrorMessage.html that has getters for all kinds of useful info: column name, constraint, table, internal position, etc. etc. We have a fn that just map-ifies the whole thing and re-throws it as an ex-info.
Thanks very much, @U01EB0V3H39. I'll have to look into those options next time I'm touching relevant code.
Since you're using next.jdbc
, it depends on clojure.java.data
so you can probably use that to turn the exception object into a hash map since it will follow all the getters automatically for you...
@U04V70XH6 that works well. Here's a comparison of tapping the error message instance produced by (.getServerErrorMessage _e)
and the hash-map version produced by j/from-java
:
tap> #object[org.postgresql.util.ServerErrorMessage 0x16929fc0 "ERROR: relation \"repo\" already exists"]
tap> {:constraint nil
:schema nil
:SQLState "42P07"
:where nil
:table nil
:internalPosition 0
:datatype nil
:file "heap.c"
:hint nil
:internalQuery nil
:column nil
:routine "heap_create_with_catalog"
:line 1200
:severity "ERROR"
:position 0
:message "relation \"repo\" already exists"
:detail nil}
Nice!