]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Add prepare_threshold connection parameter
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 7 Jan 2022 01:07:02 +0000 (02:07 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 7 Jan 2022 22:00:43 +0000 (23:00 +0100)
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.

docs/advanced/prepare.rst
docs/api/connections.rst
docs/news.rst
psycopg/psycopg/connection.py
psycopg/psycopg/connection_async.py
tests/test_prepared.py
tests/test_prepared_async.py

index 7e633fc36bbc3e2fd9315f82c1ffb13c02e17e8a..d7b273517fe3b6ddb581a992563c8a841182f95f 100644 (file)
@@ -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`.
index 233e22d5bcdf136a7f60d3af0481cecd27590c8a..8e05dc13b7b626800cb5c93c3c5e763f4d933282 100644 (file)
@@ -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::
index 3e597a15cfc0822bb3597aa519c23f6390c77f21..fcfcdb1055ab0a5866cc1b049c1910f1af121711 100644 (file)
@@ -11,9 +11,10 @@ Psycopg 3.1 (unreleased)
 ------------------------
 
 - Add :ref:`Two-Phase Commit <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
index 6af5a327b6d03813a5be948959e56e0b6e9fc41e..2b67fc135b764de48479e9b26e1fccc622e342a5 100644 (file)
@@ -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]":
index b27a44dfed4bb3bc73e6e4c5e73105d87582998d..8b1908e7269a02acf76137ae342eba144e1b8e4d 100644 (file)
@@ -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]":
index 4984b5f903385c6f0f5b7b9b0eae188b3d8068d8..42cbc46072f0ad7d35619c1b1b0cfba26eab3fcb 100644 (file)
@@ -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()
index 9528bb80f769d64f5708d38e46e5307616c79485..4315ccb46d96e7e042e8a44702b31766379cefa5 100644 (file)
@@ -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):