]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
feat: add PGconn.full_protocol_version attributes
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 5 Jul 2025 11:50:02 +0000 (13:50 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 7 Sep 2025 11:47:24 +0000 (13:47 +0200)
Wrap libpq 18 PQfullProtocolVersion functions.

See #1079

docs/conf.py
docs/news.rst
psycopg/psycopg/errors.py
psycopg/psycopg/pq/_pq_ctypes.py
psycopg/psycopg/pq/_pq_ctypes.pyi
psycopg/psycopg/pq/abc.py
psycopg/psycopg/pq/pq_ctypes.py
psycopg_c/psycopg_c/pq/libpq.pxd
psycopg_c/psycopg_c/pq/pgconn.pyx
tests/pq/test_pgconn.py

index cf87980d32f32a01eba0359c8a9678e315219adb..0d4e77dee5939612a37508e4bcf95ff3612f9cb9 100644 (file)
@@ -101,7 +101,7 @@ intersphinx_mapping = {
 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"
index 67f16c51df041fa63a208bad7e1520c859fd4f63..ab5562426e67b602b65e295a7ce96eee827e7b2d 100644 (file)
@@ -25,6 +25,8 @@ Psycopg 3.3.0 (unreleased)
 
 - Add `pq.PGconn.used_gssapi` attribute and `Capabilities.has_used_gssapi()`
   function (:ticket:`#1138`).
+- Add support for the :pq:`PQfullProtocolVersion()` function, introduced in
+  libpq v18 (:ticket:`#1079`).
 
 .. rubric:: Other changes
 
index 2ee17c45f06a23bd053553fcee13f63d12d2910f..c7dd43aadb71751e1164a66f3684ac43f17fc744 100644 (file)
@@ -61,6 +61,7 @@ class FinishedPGconn:
     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
index b02812c7103753480f555f71ccc1d7e1d01ef312..eab01f57fbc49834c480162981095c381055e5cd 100644 (file)
@@ -39,6 +39,17 @@ if sys.platform == "linux":
     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
@@ -190,16 +201,6 @@ PQhost.argtypes = [PGconn_ptr]
 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]
@@ -235,6 +236,13 @@ PQprotocolVersion = pq.PQprotocolVersion
 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
index fda7f1559a8c74089576500e919890719b7380b8..d63689551659f364a1eabedc7d045831248c7e6a 100644 (file)
@@ -165,6 +165,7 @@ def PQstatus(arg1: PGconn_struct | None) -> int: ...
 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: ...
index 65932840251de4cc81899c266abe53a79a77104a..59aa941b7b335d19b1d96b35e67b6a906fa0009d 100644 (file)
@@ -88,6 +88,9 @@ class PGconn(Protocol):
     @property
     def protocol_version(self) -> int: ...
 
+    @property
+    def full_protocol_version(self) -> int: ...
+
     @property
     def server_version(self) -> int: ...
 
index 686df557ec28fecd1f21ec397fbb8701c253fd0a..e2bc90d7cecd7119b15006d214f4c3846cd1755b 100644 (file)
@@ -237,6 +237,10 @@ class PGconn:
     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)
index d2e87cbfe7aa6509cb8be14c6f642051ff2f4c4b..2bb828ccc4dada5b8ca73da5edab94ff0f511357 100644 (file)
@@ -139,6 +139,7 @@ cdef extern from "libpq-fe.h":
     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
@@ -367,4 +368,8 @@ typedef struct pg_cancel_conn PGcancelConn;
 #define PQcancelFinish(cancelConn) 0
 #define PQsetChunkedRowsMode(conn, chunkSize) 0
 #endif
+
+#if PG_VERSION_NUM < 180000
+#define PQfullProtocolVersion(conn) 0
+#endif
 """
index 81f1aaecd88a1c1e01568cb2b4b65f8e7e90c167..3f99deb198c523a5cd126103436bece4a9090df3 100644 (file)
@@ -196,6 +196,12 @@ cdef class PGconn:
     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)
index 900e06c9d835fcd84e84e91ee3d57d9991a0e16a..4e8c57f8ef80f584f2571190996a3eb43cb508ef 100644 (file)
@@ -306,6 +306,20 @@ def test_protocol_version(pgconn):
         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()