From: Daniele Varrazzo Date: Mon, 25 Aug 2025 16:35:30 +0000 (+0200) Subject: feat: add support for libpq PQconnectionUsedGSSAPI() X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c9c8367533bb07f910b37ccd82b898cc5aa89c48;p=thirdparty%2Fpsycopg.git feat: add support for libpq PQconnectionUsedGSSAPI() We will not advertise its presence as PGconn.used_gssapi until Psycopg 3.3. However we can use it internally to implement a warning to check the implicit usage of gssencmode=prefer. See #1136 --- diff --git a/psycopg/psycopg/errors.py b/psycopg/psycopg/errors.py index fe410fa67..b71c56dd6 100644 --- a/psycopg/psycopg/errors.py +++ b/psycopg/psycopg/errors.py @@ -66,6 +66,7 @@ class FinishedPGconn: backend_pid: int = 0 needs_password: bool = False used_password: bool = False + used_gssapi: bool = False ssl_in_use: bool = False nonblocking: int = 0 diff --git a/psycopg/psycopg/pq/_pq_ctypes.py b/psycopg/psycopg/pq/_pq_ctypes.py index 99e49357c..b02812c71 100644 --- a/psycopg/psycopg/pq/_pq_ctypes.py +++ b/psycopg/psycopg/pq/_pq_ctypes.py @@ -259,6 +259,13 @@ PQconnectionUsedPassword = pq.PQconnectionUsedPassword PQconnectionUsedPassword.argtypes = [PGconn_ptr] PQconnectionUsedPassword.restype = c_int +if libpq_version >= 160000: + PQconnectionUsedGSSAPI = pq.PQconnectionUsedGSSAPI + PQconnectionUsedGSSAPI.argtypes = [PGconn_ptr] + PQconnectionUsedGSSAPI.restype = c_int +else: + PQconnectionUsedGSSAPI = not_supported_before("PQconnectionUsedGSSAPI", 160000) + PQsslInUse = pq.PQsslInUse PQsslInUse.argtypes = [PGconn_ptr] PQsslInUse.restype = c_int diff --git a/psycopg/psycopg/pq/_pq_ctypes.pyi b/psycopg/psycopg/pq/_pq_ctypes.pyi index 091ef6be7..fda7f1559 100644 --- a/psycopg/psycopg/pq/_pq_ctypes.pyi +++ b/psycopg/psycopg/pq/_pq_ctypes.pyi @@ -170,6 +170,7 @@ def PQsocket(arg1: PGconn_struct | None) -> int: ... def PQbackendPID(arg1: PGconn_struct | None) -> int: ... def PQconnectionNeedsPassword(arg1: PGconn_struct | None) -> int: ... def PQconnectionUsedPassword(arg1: PGconn_struct | None) -> int: ... +def PQconnectionUsedGSSAPI(arg1: PGconn_struct | None) -> int: ... def PQsslInUse(arg1: PGconn_struct | None) -> int: ... def PQexec(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... def PQexecParams(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> PGresult_struct: ... diff --git a/psycopg/psycopg/pq/abc.py b/psycopg/psycopg/pq/abc.py index 5fbe112b9..659328402 100644 --- a/psycopg/psycopg/pq/abc.py +++ b/psycopg/psycopg/pq/abc.py @@ -103,6 +103,9 @@ class PGconn(Protocol): @property def used_password(self) -> bool: ... + @property + def used_gssapi(self) -> bool: ... + @property def ssl_in_use(self) -> bool: ... diff --git a/psycopg/psycopg/pq/pq_ctypes.py b/psycopg/psycopg/pq/pq_ctypes.py index cf9684f5e..059f0ae45 100644 --- a/psycopg/psycopg/pq/pq_ctypes.py +++ b/psycopg/psycopg/pq/pq_ctypes.py @@ -268,6 +268,10 @@ class PGconn: """ return bool(impl.PQconnectionUsedPassword(self._pgconn_ptr)) + @property + def used_gssapi(self) -> bool: + return bool(impl.PQconnectionUsedGSSAPI(self._pgconn_ptr)) + @property def ssl_in_use(self) -> bool: return self._call_bool(impl.PQsslInUse) diff --git a/psycopg_c/psycopg_c/pq/libpq.pxd b/psycopg_c/psycopg_c/pq/libpq.pxd index ada48d674..dc7fb54cf 100644 --- a/psycopg_c/psycopg_c/pq/libpq.pxd +++ b/psycopg_c/psycopg_c/pq/libpq.pxd @@ -144,6 +144,7 @@ cdef extern from "libpq-fe.h": int PQbackendPID(const PGconn *conn) int PQconnectionNeedsPassword(const PGconn *conn) int PQconnectionUsedPassword(const PGconn *conn) + int PQconnectionUsedGSSAPI(const PGconn *conn) int PQsslInUse(PGconn *conn) # TODO: const in PG 12 docs - verify/report # TODO: PQsslAttribute, PQsslAttributeNames, PQsslStruct, PQgetssl @@ -343,6 +344,10 @@ typedef enum { #define PQsetTraceFlags(conn, stream) do {} while (0) #endif +#if PG_VERSION_NUM < 160000 +#define PQconnectionUsedGSSAPI(conn) 0 +#endif + #if PG_VERSION_NUM < 170000 typedef struct pg_cancel_conn PGcancelConn; #define PQchangePassword(conn, user, passwd) NULL diff --git a/psycopg_c/psycopg_c/pq/pgconn.pyx b/psycopg_c/psycopg_c/pq/pgconn.pyx index f8e23d4f3..81f1aaecd 100644 --- a/psycopg_c/psycopg_c/pq/pgconn.pyx +++ b/psycopg_c/psycopg_c/pq/pgconn.pyx @@ -219,6 +219,11 @@ cdef class PGconn: def used_password(self) -> bool: return bool(libpq.PQconnectionUsedPassword(self._pgconn_ptr)) + @property + def used_gssapi(self) -> bool: + _check_supported("PQconnectionUsedGSSAPI", 160000) + return bool(libpq.PQconnectionUsedGSSAPI(self._pgconn_ptr)) + @property def ssl_in_use(self) -> bool: return bool(_call_int(self, libpq.PQsslInUse)) diff --git a/tests/pq/test_pgconn.py b/tests/pq/test_pgconn.py index 75f74c463..900e06c9d 100644 --- a/tests/pq/test_pgconn.py +++ b/tests/pq/test_pgconn.py @@ -377,6 +377,17 @@ def test_used_password(pgconn, dsn, monkeypatch): pgconn.used_password +@pytest.mark.libpq(">= 16") +def test_used_gssapi(pgconn): + assert isinstance(pgconn.used_gssapi, bool) + + +@pytest.mark.libpq("< 16") +def test_used_gssapi_not_suppored(pgconn): + with pytest.raises(psycopg.NotSupportedError): + pgconn.used_gssapi + + def test_ssl_in_use(pgconn): assert isinstance(pgconn.ssl_in_use, bool)