Fork me on GitHub
#sql
<
2022-08-13
>
sheluchin17:08:59

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.
`

seancorfield18:08:09

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.

sheluchin19:08:51

@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?

seancorfield20:08:05

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.

sheluchin14:08:22

Thanks @U04V70XH6. I'll take some time to study the class hierarchy. SQLIntegrityConstraintViolationException is a helpful pointer.

Max14:11:13

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.

🙏 1
sheluchin14:11:54

Thanks very much, @U01EB0V3H39. I'll have to look into those options next time I'm touching relevant code.

seancorfield17:11:59

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...

sheluchin12:12:55

@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}