second group of digits is always 00. For example, version 9.3.5 is
returned as 90305, version 10.2 as 100002.
+ .. autoattribute:: full_protocol_version
+
+ .. versionadded:: 3.3
+
.. autoattribute:: error_message
.. automethod:: get_parameters
.. autoattribute:: port
.. autoattribute:: dbname
+
+ .. autoattribute:: service
+
+ Only available if the libpq used is from PostgreSQL 18 or newer.
+ Raise `~psycopg.NotSupportedError` otherwise. You can use the
+ `~Capabilities.has_service` capability to check for support.
+
+ .. versionadded:: 3.3
+
.. autoattribute:: user
.. autoattribute:: password
.. autoattribute:: options
.. versionadded:: 3.2
+ .. automethod:: has_full_protocol_version
+
+ .. versionadded:: 3.3
+
.. automethod:: has_encrypt_password
+
+ .. automethod:: has_service
+
+ .. versionadded:: 3.3
+
.. automethod:: has_hostaddr
.. automethod:: has_pipeline
.. automethod:: has_set_trace_flags
- Cursors are now iterators, not only iterables. This means you can call
``next(cur)`` to fetch the next row (:ticket:`#1064`).
+- Add `ConnectionInfo.full_protocol_version` and `~ConnectionInfo.service`
+ attributes (related to :ticket:`#1079`).
+- Add `~Capabilities.has_service` and `~Capabilities.has_full_protocol_version`
+ capabilities (related to :ticket:`#1079`).
- 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`).
def __init__(self) -> None:
self._cache: dict[str, str] = {}
+ def has_full_protocol_version(self, check: bool = False) -> bool:
+ """Check if the `PGconn.full_protocol_version()` method is implemented.
+
+ If the method is implemented, then `ConnectionInfo.full_protocol_version`
+ will return a meaningful value.
+
+ The feature requires libpq 18.0 and greater.
+ """
+ return self._has_feature(
+ "pq.PGconn.full_protocol_version()", 180000, check=check
+ )
+
def has_encrypt_password(self, check: bool = False) -> bool:
"""Check if the `PGconn.encrypt_password()` method is implemented.
"""
return self._has_feature("pq.PGconn.encrypt_password()", 100000, check=check)
+ def has_service(self, check: bool = False) -> bool:
+ """Check if the `ConnectionInfo.service` attribute is implemented.
+
+ The feature requires libpq 18.0 and greater.
+ """
+ return self._has_feature("Connection.info.service", 180000, check=check)
+
def has_hostaddr(self, check: bool = False) -> bool:
"""Check if the `ConnectionInfo.hostaddr` attribute is implemented.
"""The database name of the connection. See :pq:`PQdb()`."""
return self._get_pgconn_attr("db")
+ @property
+ def service(self) -> str:
+ """The service name of the connection. See :pq:`PQservice()`."""
+ return self._get_pgconn_attr("service")
+
@property
def user(self) -> str:
"""The user name of the connection. See :pq:`PQuser()`."""
"""
return self.pgconn.server_version
+ @property
+ def full_protocol_version(self) -> int:
+ """
+ An integer representing the server full protocol version.
+
+ Return a value in the format described in :pq:`PQfullProtocolVersion()`.
+
+ Only meaningful if the libpq used is version 18 or greater. If the
+ version is lesser than that, return the value reported by
+ :pq:`PQprotocolVersion()` (but in the same format as above, e.g. 30000
+ for version 3). You can use `Capabilities.has_full_protocol_version()`
+ to verify if the value can be considered reliable.
+ """
+ try:
+ return self.pgconn.full_protocol_version
+ except e.NotSupportedError:
+ return self.pgconn.protocol_version * 10000
+
@property
def backend_pid(self) -> int:
"""
pass
caps = [
+ ("has_full_protocol_version", "pq.PGconn.full_protocol_version()", 18),
("has_encrypt_password", "pq.PGconn.encrypt_password()", 10),
+ ("has_service", "Connection.info.service", 18),
("has_hostaddr", "Connection.info.hostaddr", 12),
("has_pipeline", "Connection.pipeline()", 14),
("has_set_trace_flags", "PGconn.set_trace_flags()", 14),
@pytest.mark.parametrize(
"attr",
- [("dbname", "db"), "host", "hostaddr", "user", "password", "options"],
+ [("dbname", "db"), "service", "host", "hostaddr", "user", "password", "options"],
)
def test_attrs(conn, attr):
if isinstance(attr, tuple):
if info_attr == "hostaddr" and psycopg.pq.version() < 120000:
pytest.skip("hostaddr not supported on libpq < 12")
+ if info_attr == "service" and psycopg.pq.version() < 180000:
+ pytest.skip("service not supported on libpq < 18")
+
info_val = getattr(conn.info, info_attr)
pgconn_val = getattr(conn.pgconn, pgconn_attr).decode()
assert info_val == pgconn_val
getattr(conn.info, info_attr)
+@pytest.mark.libpq("< 18")
+def test_service_not_supported(conn):
+ with pytest.raises(psycopg.NotSupportedError):
+ conn.info.service
+
+
@pytest.mark.libpq("< 12")
def test_hostaddr_not_supported(conn):
with pytest.raises(psycopg.NotSupportedError):
conn.info.port
+def test_full_protocol_version(conn):
+ assert conn.info.full_protocol_version >= 30000
+ conn.close()
+ with pytest.raises(psycopg.OperationalError):
+ conn.info.full_protocol_version
+
+
@pytest.mark.skipif(psycopg.pq.__impl__ != "python", reason="can't monkeypatch C")
def test_blank_port(conn, monkeypatch):