From: Denis Laxalde Date: Wed, 15 Dec 2021 12:57:31 +0000 (+0100) Subject: Keep results from all queries run through executemany() X-Git-Tag: pool-3.1~49^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2372daae73f78a77caeec81768283cc757c20c8;p=thirdparty%2Fpsycopg.git Keep results from all queries run through executemany() Instead of overwriting cursor's results at each execution in executemany() we now accumulate the results of all queries on cursor state by turning _set_results() into _extend_results(). This way, each result remains accessible and one can use nextset() to move from one result to another. --- diff --git a/docs/api/cursors.rst b/docs/api/cursors.rst index 46cb39be8..ea42d4dba 100644 --- a/docs/api/cursors.rst +++ b/docs/api/cursors.rst @@ -89,6 +89,9 @@ The `!Cursor` class See :ref:`query-parameters` for all the details about executing queries. + Results from each query, if any, can be walked through by calling + `nextset()`. + .. automethod:: copy :param statement: The copy operation to execute diff --git a/docs/news.rst b/docs/news.rst index 078ee5116..2b8b2fbbd 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -12,6 +12,8 @@ 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`). Current release diff --git a/psycopg/psycopg/cursor.py b/psycopg/psycopg/cursor.py index c12cf1f12..ccee73bf6 100644 --- a/psycopg/psycopg/cursor.py +++ b/psycopg/psycopg/cursor.py @@ -151,7 +151,8 @@ class BaseCursor(Generic[ConnectionType, Row]): def nextset(self) -> Optional[bool]: """ - Move to the next result set if `execute()` returned more than one. + Move to the result set of the next query executed through `executemany()` + or to the next result set if `execute()` returned more than one. Return `!True` if a new result is available, which will be the one methods `!fetch*()` will operate on. @@ -222,6 +223,7 @@ class BaseCursor(Generic[ConnectionType, Row]): results = yield from self._maybe_prepare_gen(pgq, prepare=True) self._check_results(results) + self._results.extend(results) for res in results: nrows += res.command_tuples or 0 diff --git a/tests/test_cursor.py b/tests/test_cursor.py index c4a4da872..293ff6ce4 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -282,13 +282,17 @@ def test_executemany_rowcount(conn, execmany): assert cur.rowcount == 2 -def test_executemany_returning_rowcount(conn, execmany): +def test_executemany_returning(conn, execmany): cur = conn.cursor() cur.executemany( "insert into execmany(num, data) values (%s, %s) returning num", [(10, "hello"), (20, "world")], ) assert cur.rowcount == 2 + assert cur.fetchone() == (10,) + assert cur.nextset() + assert cur.fetchone() == (20,) + assert cur.nextset() is None def test_executemany_rowcount_no_hit(conn, execmany): diff --git a/tests/test_cursor_async.py b/tests/test_cursor_async.py index 9a73f8456..da3f44290 100644 --- a/tests/test_cursor_async.py +++ b/tests/test_cursor_async.py @@ -274,13 +274,17 @@ async def test_executemany_rowcount(aconn, execmany): assert cur.rowcount == 2 -async def test_executemany_returning_rowcount(aconn, execmany): +async def test_executemany_returning(aconn, execmany): cur = aconn.cursor() await cur.executemany( "insert into execmany(num, data) values (%s, %s) returning num", [(10, "hello"), (20, "world")], ) assert cur.rowcount == 2 + assert (await cur.fetchone()) == (10,) + assert cur.nextset() + assert (await cur.fetchone()) == (20,) + assert cur.nextset() is None async def test_executemany_rowcount_no_hit(aconn, execmany):