postgres · API

postgres.sqlstate

SQLSTATE handling: structured errors, and the codes libpq reports.

Every error a PostgreSQL server sends carries a five-character SQLSTATE code (23505, 40P01, ...) alongside a human-readable message — see https://www.postgresql.org/docs/current/errcodes-appendix.html. Mojo 1.x can only raise Error(String), so there is no field to hang the code on when an error crosses a raises boundary. Instead this module packs the code into a parseable token inside the message text ("[SQLSTATE 23505]"), and gives callers two ways back out:

  • PostgresError is the structured value: build one from a libpq result, call .to_error() to raise it, and keep the original struct around wherever a try/except block has an Error in hand and wants the fields back — reconstruct a PostgresError(...), or just pull the code via sqlstate_of(err).
  • sqlstate_of() recovers the raw code from an Error or a String even when the PostgresError itself is gone — the common case, since Mojo's except err: only ever hands back an Error.

08001 and 08006 are not real server SQLSTATEs — libpq itself reports no SQLSTATE at all for a connection that never came up or that dropped mid-use (PQerrorMessage on a failed/lost connection carries only free text). The FFI layer synthesizes SQLCLIENT_UNABLE_TO_ESTABLISH (08001) for a failed PQconnectdb and CONNECTION_FAILURE (08006) for a connection lost after it was established, mirroring the meaning class 08 has for every other client.

Structs

PostgresError

struct PostgresError

A PostgreSQL error, decomposed the way PQresultErrorField reports it: severity, SQLSTATE, primary message, and the optional detail/hint the server adds to explain or suggest a fix.

All fields are String and empty when the server didn't supply them. sql is not one of libpq's fields — it's the statement text the caller was executing, attached here so the formatted error is self-contained in a log line.

Implements Copyable, Movable, Writable

Fields
severity: String: ERROR, FATAL, PANIC, or a localized equivalent. Empty if unknown.
sqlstate: String: The five-character SQLSTATE code, e.g. 23505. Empty if the source of this error didn't have one (see the module docstring).
message: String: The primary human-readable message.
detail: String: An optional secondary message with more detail. Empty if absent.
hint: String: An optional suggestion of how to fix the problem. Empty if absent.
sql: String: The SQL text being executed when the error occurred. Empty if not attached.

__init__

def __init__(out self, severity: String = "", sqlstate: String = "", message: String = "", detail: String = "", hint: String = "", sql: String = "")

format

def format(self) -> String

Render the multi-line form this type prints and raises as.

Line one is "postgres [SQLSTATE 23505] <message>", or "postgres: <message>" when there's no SQLSTATE to report. A DETAIL: line follows if detail is set, then a HINT: line if hint is set, then a SQL: line — collapsed to one line and capped at 200 characters — if sql is set.

__str__

def __str__(self) -> String

Same as format().

write_to

def write_to(self, mut writer: T)

Write format() to writer — what makes String(err) and print(err) produce the formatted form.

to_error

def to_error(self) -> Error

Wrap format() in an Error, ready to raise.

sqlstate_of() recovers self.sqlstate back out of the raised Error on the far side of a try/except.

is_unique_violation

def is_unique_violation(self) -> Bool

See the free function of the same name.

is_serialization_failure

def is_serialization_failure(self) -> Bool

See the free function of the same name.

is_deadlock

def is_deadlock(self) -> Bool

See the free function of the same name.

is_integrity_violation

def is_integrity_violation(self) -> Bool

See the free function of the same name.

is_connection_error

def is_connection_error(self) -> Bool

See the free function of the same name.

is_retryable

def is_retryable(self) -> Bool

See the free function of the same name.

Functions

sqlstate_of

def sqlstate_of(message: String) -> String

Extract the code inside the first "[SQLSTATE XXXXX]" token in message.

Returns "" if no such token is present, or if what's between the brackets isn't exactly five uppercase-alphanumeric characters (a truncated or hand-edited message).

def sqlstate_of(err: Error) -> String

Extract the SQLSTATE code from a caught Error — the usual case, since Mojo's except err: hands back only an Error. See the String overload for the parsing rules.

sqlstate_class

def sqlstate_class(code: String) -> String

The two-character SQLSTATE class ("23" for "23505"), or "" if code is shorter than two characters.

The class is the coarse-grained grouping PostgreSQL itself uses in the errcodes appendix — class 23 is every integrity-constraint violation, class 08 is every connection error, and so on.

is_unique_violation

def is_unique_violation(code: String) -> Bool

True for UNIQUE_VIOLATION (23505).

is_serialization_failure

def is_serialization_failure(code: String) -> Bool

True for SERIALIZATION_FAILURE (40001), the code a SERIALIZABLE transaction gets when it loses a conflict.

is_deadlock

def is_deadlock(code: String) -> Bool

True for DEADLOCK_DETECTED (40P01).

is_integrity_violation

def is_integrity_violation(code: String) -> Bool

True for class 23 — every constraint violation (unique, foreign key, not-null, check).

is_connection_error

def is_connection_error(code: String) -> Bool

True for class 08 — every connection-establishment or connection-loss error, including the synthesized 08001/08006 documented at the top of this module.

is_retryable

def is_retryable(code: String) -> Bool

True for SERIALIZATION_FAILURE or DEADLOCK_DETECTED — the two codes PostgreSQL's own docs recommend retrying the whole transaction for, rather than surfacing to the caller.

Aliases

comptime UNIQUE_VIOLATION = StringSpan("23505")
comptime FOREIGN_KEY_VIOLATION = StringSpan("23503")
comptime NOT_NULL_VIOLATION = StringSpan("23502")
comptime CHECK_VIOLATION = StringSpan("23514")
comptime SERIALIZATION_FAILURE = StringSpan("40001")
comptime DEADLOCK_DETECTED = StringSpan("40P01")
comptime UNDEFINED_TABLE = StringSpan("42P01")
comptime UNDEFINED_COLUMN = StringSpan("42703")
comptime SYNTAX_ERROR = StringSpan("42601")
comptime INSUFFICIENT_PRIVILEGE = StringSpan("42501")
comptime DUPLICATE_TABLE = StringSpan("42P07")
comptime QUERY_CANCELED = StringSpan("57014")
comptime INVALID_TEXT_REPRESENTATION = StringSpan("22P02")
comptime NUMERIC_VALUE_OUT_OF_RANGE = StringSpan("22003")
comptime DIVISION_BY_ZERO = StringSpan("22012")
comptime LOCK_NOT_AVAILABLE = StringSpan("55P03")
comptime ADMIN_SHUTDOWN = StringSpan("57P01")
comptime CONNECTION_FAILURE = StringSpan("08006")
comptime SQLCLIENT_UNABLE_TO_ESTABLISH = StringSpan("08001")
comptime CONNECTION_DOES_NOT_EXIST = StringSpan("08003")

Raised by pool.ConnectionPool.lease for a lease taken after pool.ConnectionPool.close.

comptime TOO_MANY_CONNECTIONS = StringSpan("53300")

Raised by pool.ConnectionPool.lease when pool.PoolConfig.acquire_timeout_ms elapses with every connection checked out. The server uses this same code when it runs out of backends, which is the same condition one layer down.