Wrap libpq 18 PQfullProtocolVersion and PQservice functions.
See #1079
autodoc_member_order = "bysource"
# PostgreSQL docs version to link libpq functions to
-libpq_docs_version = "17"
+libpq_docs_version = "18"
# Where to point on :ticket: role
ticket_url = "https://github.com/psycopg/psycopg/issues/%s"
- Cursors are now iterators, not only iterables. This means you can call
``next(cur)`` to fetch the next row (:ticket:`#1064`).
+- Add support for functions :pq:`PQfullProtocolVersion`, :pq:`PQservice`,
+ available in libpq v18 (:ticket:`#1079`).
- Drop support for Python 3.8 (:ticket:`#976`) and 3.9 (:ticket:`#1056`).
info: list[ConninfoOption] = field(default_factory=list)
db: bytes = b""
+ service: bytes = b""
user: bytes = b""
password: bytes = b""
host: bytes = b""
error_message: bytes = b""
_encoding: str = "utf-8"
protocol_version: int = 0
+ full_protocol_version: int = 0
server_version: int = 0
backend_pid: int = 0
fdopen.argtypes = (c_int, c_char_p)
fdopen.restype = FILE_ptr
+
+def not_supported_before(fname: str, pgversion: int) -> Any:
+ def not_supported(*args: Any, **kwargs: Any) -> NoReturn:
+ raise NotSupportedError(
+ f"{fname} requires libpq from PostgreSQL {version_pretty(pgversion)} on"
+ f" the client; version {version_pretty(libpq_version)} available instead"
+ )
+
+ return not_supported
+
+
# Get the libpq version to define what functions are available.
PQlibVersion = pq.PQlibVersion
PQdb.argtypes = [PGconn_ptr]
PQdb.restype = c_char_p
+if libpq_version >= 180000:
+ PQservice = pq.PQservice
+ PQservice.argtypes = [PGconn_ptr]
+ PQservice.restype = c_char_p
+else:
+ PQservice = not_supported_before("PQservice", 180000)
+
PQuser = pq.PQuser
PQuser.argtypes = [PGconn_ptr]
PQuser.restype = c_char_p
PQhost.restype = c_char_p
-def not_supported_before(fname: str, pgversion: int) -> Any:
- def not_supported(*args: Any, **kwargs: Any) -> NoReturn:
- raise NotSupportedError(
- f"{fname} requires libpq from PostgreSQL {version_pretty(pgversion)} on"
- f" the client; version {version_pretty(libpq_version)} available instead"
- )
-
- return not_supported
-
-
if libpq_version >= 120000:
PQhostaddr = pq.PQhostaddr
PQhostaddr.argtypes = [PGconn_ptr]
PQprotocolVersion.argtypes = [PGconn_ptr]
PQprotocolVersion.restype = c_int
+if libpq_version >= 180000:
+ PQfullProtocolVersion = pq.PQfullProtocolVersion
+ PQfullProtocolVersion.argtypes = [PGconn_ptr]
+ PQfullProtocolVersion.restype = c_int
+else:
+ PQfullProtocolVersion = not_supported_before("PQfullProtocolVersion", 180000)
+
PQserverVersion = pq.PQserverVersion
PQserverVersion.argtypes = [PGconn_ptr]
PQserverVersion.restype = c_int
def PQresetPoll(arg1: PGconn_struct | None) -> int: ...
def PQping(arg1: bytes) -> int: ...
def PQdb(arg1: PGconn_struct | None) -> bytes | None: ...
+def PQservice(arg1: PGconn_struct | None) -> bytes | None: ...
def PQuser(arg1: PGconn_struct | None) -> bytes | None: ...
def PQpass(arg1: PGconn_struct | None) -> bytes | None: ...
def PQhost(arg1: PGconn_struct | None) -> bytes | None: ...
def PQtransactionStatus(arg1: PGconn_struct | None) -> int: ...
def PQparameterStatus(arg1: PGconn_struct | None, arg2: bytes) -> bytes | None: ...
def PQprotocolVersion(arg1: PGconn_struct | None) -> int: ...
+def PQfullProtocolVersion(arg1: PGconn_struct | None) -> int: ...
def PQserverVersion(arg1: PGconn_struct | None) -> int: ...
def PQsocket(arg1: PGconn_struct | None) -> int: ...
def PQbackendPID(arg1: PGconn_struct | None) -> int: ...
@property
def db(self) -> bytes: ...
+ @property
+ def service(self) -> bytes: ...
+
@property
def user(self) -> bytes: ...
@property
def protocol_version(self) -> int: ...
+ @property
+ def full_protocol_version(self) -> int: ...
+
@property
def server_version(self) -> int: ...
def db(self) -> bytes:
return self._call_bytes(impl.PQdb)
+ @property
+ def service(self) -> bytes:
+ return self._call_bytes(impl.PQservice)
+
@property
def user(self) -> bytes:
return self._call_bytes(impl.PQuser)
def protocol_version(self) -> int:
return self._call_int(impl.PQprotocolVersion)
+ @property
+ def full_protocol_version(self) -> int:
+ return self._call_int(impl.PQfullProtocolVersion)
+
@property
def server_version(self) -> int:
return self._call_int(impl.PQserverVersion)
# 33.2. Connection Status Functions
char *PQdb(const PGconn *conn)
+ char *PQservice(const PGconn *conn)
char *PQuser(const PGconn *conn)
char *PQpass(const PGconn *conn)
char *PQhost(const PGconn *conn)
PGTransactionStatusType PQtransactionStatus(const PGconn *conn)
const char *PQparameterStatus(const PGconn *conn, const char *paramName)
int PQprotocolVersion(const PGconn *conn)
+ int PQfullProtocolVersion(const PGconn *conn)
int PQserverVersion(const PGconn *conn)
char *PQerrorMessage(const PGconn *conn)
int PQsocket(const PGconn *conn) nogil
#define PQcancelFinish(cancelConn) 0
#define PQsetChunkedRowsMode(conn, chunkSize) 0
#endif
+
+#if PG_VERSION_NUM < 180000
+#define PQfullProtocolVersion(conn) 0
+#define PQservice(conn) NULL
+#endif
"""
def db(self) -> bytes:
return _call_bytes(self, libpq.PQdb)
+ @property
+ def service(self) -> bytes:
+ _check_supported("PQservice", 180000)
+ _ensure_pgconn(self)
+ cdef char *rv = libpq.PQservice(self._pgconn_ptr)
+ return rv if rv is not NULL else b""
+
@property
def user(self) -> bytes:
return _call_bytes(self, libpq.PQuser)
def protocol_version(self) -> int:
return _call_int(self, libpq.PQprotocolVersion)
+ @property
+ def full_protocol_version(self) -> int:
+ _check_supported("PQfullProtocolVersion", 180000)
+ _ensure_pgconn(self)
+ return libpq.PQfullProtocolVersion(self._pgconn_ptr)
+
@property
def server_version(self) -> int:
return _call_int(self, libpq.PQserverVersion)
pgconn.db
+@pytest.mark.libpq(">= 18")
+def test_service(pgconn):
+ assert isinstance(pgconn.service, bytes)
+
+
+@pytest.mark.libpq("< 18")
+def test_service_notimpl(pgconn):
+ with pytest.raises(psycopg.NotSupportedError):
+ pgconn.service
+
+
def test_user(pgconn):
user = [o.val for o in pgconn.info if o.keyword == b"user"][0]
assert pgconn.user == user
pgconn.protocol_version
+@pytest.mark.libpq(">= 18")
+def test_full_protocol_version(pgconn):
+ assert pgconn.full_protocol_version >= 30000
+ pgconn.finish()
+ with pytest.raises(psycopg.OperationalError):
+ pgconn.full_protocol_version
+
+
+@pytest.mark.libpq("< 18")
+def test_full_protocol_version_notimpl(pgconn):
+ with pytest.raises(psycopg.NotSupportedError):
+ pgconn.full_protocol_version
+
+
def test_server_version(pgconn):
assert pgconn.server_version >= 90400
pgconn.finish()