]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Keep results from all queries run through executemany()
authorDenis Laxalde <denis.laxalde@dalibo.com>
Wed, 15 Dec 2021 12:57:31 +0000 (13:57 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 2 Jan 2022 20:07:40 +0000 (21:07 +0100)
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.

docs/api/cursors.rst
docs/news.rst
psycopg/psycopg/cursor.py
tests/test_cursor.py
tests/test_cursor_async.py

index 46cb39be81abcde4ad8e31827886c44f123149ee..ea42d4dbae052dce94283a49f18d0fe6834207c5 100644 (file)
@@ -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
index 078ee51160e97f13a40bd960c0a58ede5ab093ff..2b8b2fbbd9cf347d33efb0b91480eeb270661ef6 100644 (file)
@@ -12,6 +12,8 @@ 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`).
 
 
 Current release
index c12cf1f1200f537335bea233f903e270c29b2051..ccee73bf64736a7d17656c4b127fe9d07a4bf51e 100644 (file)
@@ -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
index c4a4da8727b54d8058f89cc912d67ddb02dc3fa9..293ff6ce44d6fc36bd979988a6d9fe5e735c85f8 100644 (file)
@@ -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):
index 9a73f8456b0b80d2e92a850b883dc757d3536ce6..da3f442903f9daf129d7a39e5c29f06331c43db8 100644 (file)
@@ -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):