From: Daniele Varrazzo Date: Tue, 1 Aug 2023 10:57:48 +0000 (+0100) Subject: fix: raise a warning if nextset is used after execute in pipeline mode X-Git-Tag: 3.1.10~4^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d684f489d95094fa93f884354ddc8494bc204e2c;p=thirdparty%2Fpsycopg.git fix: raise a warning if nextset is used after execute in pipeline mode So far have accumulated results, unlike in no-pipeline mode, where results have been discarded. But this proved to be not reliable, so we will forbid it in 3.2. See #604 --- diff --git a/psycopg/psycopg/cursor.py b/psycopg/psycopg/cursor.py index 6048bbf66..349f6ef5c 100644 --- a/psycopg/psycopg/cursor.py +++ b/psycopg/psycopg/cursor.py @@ -9,6 +9,7 @@ from types import TracebackType from typing import Any, Generic, Iterable, Iterator, List from typing import Optional, NoReturn, Sequence, Tuple, Type, TypeVar from typing import overload, TYPE_CHECKING +from warnings import warn from contextlib import contextmanager from . import pq @@ -156,6 +157,18 @@ class BaseCursor(Generic[ConnectionType, Row]): Return `!True` if a new result is available, which will be the one methods `!fetch*()` will operate on. """ + # Raise a warning if people is calling nextset() in pipeline mode + # after a sequence of execute() in pipeline mode. Pipeline accumulating + # execute() results in the cursor is an unintended difference w.r.t. + # non-pipeline mode. + if self._execmany_returning is None and self._conn._pipeline: + warn( + "using nextset() in pipeline mode for several execute() is" + " deprecated and will be dropped in 3.2; please use different" + " cursors to receive more than one result", + DeprecationWarning, + ) + if self._iresult < len(self._results) - 1: self._select_current_result(self._iresult + 1) return True diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 0a0e93fab..12180bdeb 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -593,3 +593,23 @@ def test_concurrency(conn): assert s == sum(values) (after,) = conn.execute("select value from accessed").fetchone() assert after > before + + +def test_execute_nextset_warning(conn): + cur = conn.cursor() + cur.execute("select 1") + cur.execute("select 2") + + assert cur.fetchall() == [(2,)] + assert not cur.nextset() + assert cur.fetchall() == [] + + with conn.pipeline(): + cur.execute("select 1") + cur.execute("select 2") + + # WARNING: this behavior is unintentional and will be changed in 3.2 + assert cur.fetchall() == [(1,)] + with pytest.warns(DeprecationWarning, match="nextset"): + assert cur.nextset() + assert cur.fetchall() == [(2,)] diff --git a/tests/test_pipeline_async.py b/tests/test_pipeline_async.py index 488036a7e..484cce4dc 100644 --- a/tests/test_pipeline_async.py +++ b/tests/test_pipeline_async.py @@ -601,3 +601,23 @@ async def test_concurrency(aconn): assert s == sum(values) (after,) = await (await aconn.execute("select value from accessed")).fetchone() assert after > before + + +async def test_execute_nextset_warning(aconn): + cur = aconn.cursor() + await cur.execute("select 1") + await cur.execute("select 2") + + assert (await cur.fetchall()) == [(2,)] + assert not cur.nextset() + assert (await cur.fetchall()) == [] + + async with aconn.pipeline(): + await cur.execute("select 1") + await cur.execute("select 2") + + # WARNING: this behavior is unintentional and will be changed in 3.2 + assert (await cur.fetchall()) == [(1,)] + with pytest.warns(DeprecationWarning, match="nextset"): + assert cur.nextset() + assert (await cur.fetchall()) == [(2,)]