Errors#
A statement the server refuses raises psycopg’s exception for that refusal, by SQLSTATE, from
psycopg.errors, raised by psycopg itself. The driver does not wrap it: except psycopg.errors.UniqueViolation catches a duplicate key, and exc.sqlstate and exc.diag hold what
the server said. The server mints no SQLSTATE of its own for graph operations, so every class you
know from PostgreSQL is the one you get.
The driver’s own classes#
Three refusals cannot be told apart by their SQLSTATE, so the driver raises a class of its own for each, a subclass of what psycopg would have raised:
Class |
Raised when |
Subclass of |
|---|---|---|
|
the server refuses because of a setting: |
|
|
a graph write in a read-only transaction |
|
|
something this server’s version does not have; |
|
>>> conn.execute("INSERT INTO moviekb.movie (properties) VALUES ('{}')")
Traceback (most recent call last):
...
agensgraph.errors.ConfigurationError: DML query to graph objects is not allowed: plain SQL data modification of a label table is refused while enable_graph_dml is off, which is its default and which only a superuser may change; write through Cypher instead
>>> conn.rollback()
And for the driver’s own failures:
Class |
Raised when |
|---|---|
|
a composite-rendering read meets a label the connection’s label table does not have; |
|
|
|
a pooled connection used after it went back to the pool |
|
a pool could not find a connection of its current generation |
|
a |
|
a deadline ran out before anything was sent; a |
All of them are in agensgraph.errors, which also re-exports psycopg’s PEP 249 hierarchy as the very
same classes, so except agensgraph.errors.IntegrityError and except psycopg.IntegrityError catch
the same exceptions. BatchFailed and Expired are also importable from agensgraph itself.
A value the driver refuses before sending, such as a NaN in a property map or a parameter for the
length of a walk, raises ValueError and never reaches the server.
What a failure prints as#
PostgreSQL puts row data in a failure’s DETAIL. A uniqueness failure says which value was a
duplicate, so a plain logger.exception() would write into the log a value the application never
meant to log. The driver leaves DETAIL and CONTEXT out of the message by default:
>>> import psycopg
>>> conn.execute("CREATE UNIQUE PROPERTY INDEX movie_title_unique ON movie (title)")
<agensgraph.connection.Cursor ...>
>>> try:
... conn.execute_query("CREATE (:movie {title: %s})", ("Inception",))
... except psycopg.errors.UniqueViolation as exc:
... duplicate = exc
>>> conn.rollback()
>>> str(duplicate)
'duplicate key value violates unique constraint "movie_title_unique"'
>>> duplicate.diag.message_detail
'Key ((properties.\'title\'))=("Inception") already exists.'
The data is still there, in exc.diag, for a post-mortem; only what the exception prints as is cut.
Anything after the first line of the message goes: DETAIL, CONTEXT and the LINE marker libpq adds.
To have it printed, for development, turn it on for the whole process:
agensgraph.show_error_details(True)
showing_error_details() reports the setting, and redact_details(exc) cuts one exception you hold.
The setting applies to failures raised by statements, streams and the bulk loaders; a failure raised by
commit() itself, such as a deferred constraint, is psycopg’s and printed in full.
Handing a failure to someone else#
To report a failure to someone who did not send the statement, in an API response, a tool’s result,
or back to a language model that wrote the query, send the SQLSTATE and the server’s first line and
nothing else. safe_message() is that:
>>> agensgraph.safe_message(duplicate)
'23505: duplicate key value violates unique constraint "movie_title_unique"'
It never includes DETAIL, it is always one line, show_error_details() does not change it, and it
works for any exception: one without a SQLSTATE, such as the driver’s own, gives its first line.
agensgraph.errors.mask_dsn() hides the password in a connection string you want to log. Nothing in the
driver puts a connection string in a message, a log record or a repr.
Whether to try again#
agensgraph.retryability(exc) classifies any exception into what to do next, from SAFE to FATAL;
see Retries.