DiagnosticField,
)
from .encodings import py_codecs
+from .misc import error_message
from . import pq_ctypes as pq_module
PGconn = pq_module.PGconn
+PGresult = pq_module.PGresult
PQerror = pq_module.PQerror
Conninfo = pq_module.Conninfo
"PGconn",
"Conninfo",
"PQerror",
+ "error_message",
"py_codecs",
)
--- /dev/null
+"""
+Various functionalities to make easier to work with the libpq.
+"""
+
+# Copyright (C) 2020 The Psycopg Team
+
+
+def error_message(obj):
+ """
+ Return an error message from a PGconn or PGresult.
+
+ The return value is a str (unlike pq data which is usually bytes).
+ """
+ from psycopg3 import pq
+
+ if isinstance(obj, pq.PGconn):
+ msg = obj.error_message
+
+ # strip severity and whitespaces
+ if msg:
+ msg = msg.splitlines()[0].split(b":", 1)[-1].strip()
+
+ elif isinstance(obj, pq.PGresult):
+ msg = obj.error_field(pq.DiagnosticField.PG_DIAG_MESSAGE_PRIMARY)
+ if not msg:
+ msg = obj.error_message
+
+ # strip severity and whitespaces
+ if msg:
+ msg = msg.splitlines()[0].split(b":", 1)[-1].strip()
+
+ else:
+ raise TypeError(
+ f"PGconn or PGresult expected, got {type(obj).__name__}"
+ )
+
+ if msg:
+ msg = msg.decode("utf8", "replace") # TODO: or in connection encoding?
+ else:
+ msg = "no details available"
+
+ return msg
TransactionStatus,
Ping,
)
+from .misc import error_message
from . import _pq_ctypes as impl
from ..exceptions import OperationalError
def error_message(self):
return impl.PQerrorMessage(self.pgconn_ptr)
- @property
- def error_str(self):
- rv = self.error_message
- if rv:
- return rv.encode('utf8', 'replace').rstrip()
- else:
- return "no details available"
-
@property
def socket(self):
return impl.PQsocket(self.pgconn_ptr)
def consume_input(self):
if 1 != impl.PQconsumeInput(self.pgconn_ptr):
- raise PQerror(f"consuming input failed: {self.error_str}")
+ raise PQerror(f"consuming input failed: {error_message(self)}")
def is_busy(self):
return impl.PQisBusy(self.pgconn_ptr)
@nonblocking.setter
def nonblocking(self, arg):
if 0 > impl.PQsetnonblocking(self.pgconn_ptr, arg):
- raise PQerror(f"setting nonblocking failed: {self.error_str}")
+ raise PQerror(f"setting nonblocking failed: {error_message(self)}")
def flush(self):
rv = impl.PQflush(self.pgconn_ptr)
if rv < 0:
- raise PQerror(f"flushing failed: {self.error_str}")
+ raise PQerror(f"flushing failed: {error_message(self)}")
return rv
if not errmsg:
raise MemoryError("couldn't allocate on conninfo parse")
else:
- exc = PQerror(errmsg.value)
+ exc = PQerror(errmsg.value.decode("utf8", "replace"))
impl.PQfreemem(errmsg)
raise exc
--- /dev/null
+import pytest
+
+
+def test_error_message(pq, pgconn):
+ res = pgconn.exec_(b"wat")
+ assert res.status == pq.ExecStatus.PGRES_FATAL_ERROR
+ msg = pq.error_message(pgconn)
+ assert msg == 'syntax error at or near "wat"'
+ assert msg == pq.error_message(res)
+ assert msg == res.error_field(
+ pq.DiagnosticField.PG_DIAG_MESSAGE_PRIMARY
+ ).decode("ascii")
+
+ with pytest.raises(TypeError):
+ pq.error_message(None)