From: Daniele Varrazzo Date: Fri, 7 Jan 2022 01:07:02 +0000 (+0100) Subject: Add prepare_threshold connection parameter X-Git-Tag: pool-3.1~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac600c5e85a98a06f984e58c16247b985ba2a2c9;p=thirdparty%2Fpsycopg.git Add prepare_threshold connection parameter This can help configuring connections to use PgBouncer. It is possible to use the attribute in the pool kwargs, for instance, instead of using the more complex configure callback. Close #200. --- diff --git a/docs/advanced/prepare.rst b/docs/advanced/prepare.rst index 7e633fc36..d7b273517 100644 --- a/docs/advanced/prepare.rst +++ b/docs/advanced/prepare.rst @@ -33,6 +33,10 @@ Statement preparation can be controlled in several ways: - You can disable the use of prepared statements on a connection by setting its `~Connection.prepare_threshold` attribute to `!None`. +.. versionchanged:: 3.1 + You can set `!prepare_threshold` as a `~Connection.connect()` keyword + parameter too. + .. seealso:: The `PREPARE`__ PostgreSQL documentation contains plenty of details about @@ -43,3 +47,11 @@ Statement preparation can be controlled in several ways: ones exposed by :pq:`PQsendPrepare`, :pq:`PQsendQueryPrepared`. .. __: https://www.postgresql.org/docs/current/sql-prepare.html + +.. warning:: + + Using external connection poolers, such as PgBouncer, is not compatible + with prepared statements, because the same client connection may change + the server session it refers to. If such middleware is used you should + disable connection pooling, by setting the `Connection.prepare_threshold` + attribute to `!None`. diff --git a/docs/api/connections.rst b/docs/api/connections.rst index 233e22d5b..8e05dc13b 100644 --- a/docs/api/connections.rst +++ b/docs/api/connections.rst @@ -42,6 +42,8 @@ The `!Connection` class to create fetching data (default: `~psycopg.rows.tuple_row()`). See :ref:`row-factories` for details. + :param prepare_threshold: Set the `prepare_threshold` attribute of the + connection. More specialized use: @@ -64,6 +66,9 @@ The `!Connection` class .. __: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS .. __: https://www.postgresql.org/docs/current/libpq-envars.html + .. versionchanged:: 3.1 + Added *prepare_threshold* parameter. + .. automethod:: close .. note:: diff --git a/docs/news.rst b/docs/news.rst index 3e597a15c..fcfcdb105 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -11,9 +11,10 @@ Psycopg 3.1 (unreleased) ------------------------ - Add :ref:`Two-Phase Commit ` support (:ticket:`#72`). -- Add `pq.PGconn.trace()` and related trace functions (:ticket:`#167`). - Return results from all queries run through `~Cursor.executemany()`; each result set can be accessed by calling `~Cursor.nextset()` (:ticket:`#164`). +- Add `pq.PGconn.trace()` and related trace functions (:ticket:`#167`). +- Add *prepare_threshold* parameter to `Connection` init (:ticket:`#200`). Current release diff --git a/psycopg/psycopg/connection.py b/psycopg/psycopg/connection.py index 6af5a327b..2b67fc135 100644 --- a/psycopg/psycopg/connection.py +++ b/psycopg/psycopg/connection.py @@ -622,6 +622,7 @@ class Connection(BaseConnection[Row]): *, autocommit: bool = False, row_factory: RowFactory[Row], + prepare_threshold: Optional[int] = 5, context: Optional[AdaptContext] = None, **kwargs: Union[None, int, str], ) -> "Connection[Row]": @@ -634,6 +635,7 @@ class Connection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, + prepare_threshold: Optional[int] = 5, context: Optional[AdaptContext] = None, **kwargs: Union[None, int, str], ) -> "Connection[TupleRow]": @@ -645,6 +647,7 @@ class Connection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, + prepare_threshold: Optional[int] = 5, row_factory: Optional[RowFactory[Row]] = None, context: Optional[AdaptContext] = None, **kwargs: Any, @@ -667,6 +670,7 @@ class Connection(BaseConnection[Row]): rv.row_factory = row_factory if context: rv._adapters = AdaptersMap(context.adapters) + rv.prepare_threshold = prepare_threshold return rv def __enter__(self) -> "Connection[Row]": diff --git a/psycopg/psycopg/connection_async.py b/psycopg/psycopg/connection_async.py index b27a44dfe..8b1908e72 100644 --- a/psycopg/psycopg/connection_async.py +++ b/psycopg/psycopg/connection_async.py @@ -64,6 +64,7 @@ class AsyncConnection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, + prepare_threshold: Optional[int] = 5, row_factory: AsyncRowFactory[Row], context: Optional[AdaptContext] = None, **kwargs: Union[None, int, str], @@ -77,6 +78,7 @@ class AsyncConnection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, + prepare_threshold: Optional[int] = 5, context: Optional[AdaptContext] = None, **kwargs: Union[None, int, str], ) -> "AsyncConnection[TupleRow]": @@ -88,6 +90,7 @@ class AsyncConnection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, + prepare_threshold: Optional[int] = 5, context: Optional[AdaptContext] = None, row_factory: Optional[AsyncRowFactory[Row]] = None, **kwargs: Any, @@ -118,6 +121,7 @@ class AsyncConnection(BaseConnection[Row]): rv.row_factory = row_factory if context: rv._adapters = AdaptersMap(context.adapters) + rv.prepare_threshold = prepare_threshold return rv async def __aenter__(self) -> "AsyncConnection[Row]": diff --git a/tests/test_prepared.py b/tests/test_prepared.py index 4984b5f90..42cbc4607 100644 --- a/tests/test_prepared.py +++ b/tests/test_prepared.py @@ -7,6 +7,14 @@ from decimal import Decimal import pytest +import psycopg + + +@pytest.mark.parametrize("value", [None, 0, 3]) +def test_prepare_threshold_init(dsn, value): + with psycopg.connect(dsn, prepare_threshold=value) as conn: + assert conn.prepare_threshold == value + def test_dont_prepare(conn): cur = conn.cursor() diff --git a/tests/test_prepared_async.py b/tests/test_prepared_async.py index 9528bb80f..4315ccb46 100644 --- a/tests/test_prepared_async.py +++ b/tests/test_prepared_async.py @@ -7,9 +7,19 @@ from decimal import Decimal import pytest +import psycopg + pytestmark = pytest.mark.asyncio +@pytest.mark.parametrize("value", [None, 0, 3]) +async def test_prepare_threshold_init(dsn, value): + async with await psycopg.AsyncConnection.connect( + dsn, prepare_threshold=value + ) as conn: + assert conn.prepare_threshold == value + + async def test_dont_prepare(aconn): cur = aconn.cursor() for i in range(10):