From: Daniele Varrazzo Date: Mon, 9 May 2022 13:23:51 +0000 (+0200) Subject: fix: fix nested transaction entering in pipeline mode X-Git-Tag: 3.1~113^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=963a6f4f5584d941c63d5b8d85b79f34c1e4ebb1;p=thirdparty%2Fpsycopg.git fix: fix nested transaction entering in pipeline mode If the connection is idle, it might be that we are missing results. Receive them to align the connection state to what the transaction object expects, otherwise the assert in _push_savepoint() will fail. --- diff --git a/psycopg/psycopg/transaction.py b/psycopg/psycopg/transaction.py index 35ef973d8..3477f8cfe 100644 --- a/psycopg/psycopg/transaction.py +++ b/psycopg/psycopg/transaction.py @@ -90,6 +90,16 @@ class BaseTransaction(Generic[ConnectionType]): raise TypeError("transaction blocks can be used only once") self._entered = True + # If we are in pipeline mode and connection idle, we might be in a + # nested statement but we haven't received yet the result to go + # INTRANS. This would make _push_savepoint() fail. If so, synchronize + # with the server. + if ( + self._conn._pipeline + and self.pgconn.transaction_status == TransactionStatus.IDLE + ): + yield from self._conn._pipeline._sync_gen() + self._push_savepoint() for command in self._get_enter_commands(): yield from self._conn._exec_command(command) diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index ba3d3d6ef..b20359f8c 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -398,6 +398,16 @@ def test_transaction_nested(conn): assert r == "inner" +def test_transaction_nested_no_statement(conn): + with conn.pipeline(): + with conn.transaction(): + with conn.transaction(): + cur = conn.execute("select 1") + + (r,) = cur.fetchone() + assert r == 1 + + def test_outer_transaction(conn): with conn.transaction(): with conn.pipeline(): diff --git a/tests/test_pipeline_async.py b/tests/test_pipeline_async.py index 6f774a5bd..5d7e669dd 100644 --- a/tests/test_pipeline_async.py +++ b/tests/test_pipeline_async.py @@ -400,6 +400,16 @@ async def test_transaction_nested(aconn): assert r == "inner" +async def test_transaction_nested_no_statement(aconn): + async with aconn.pipeline(): + async with aconn.transaction(): + async with aconn.transaction(): + cur = await aconn.execute("select 1") + + (r,) = await cur.fetchone() + assert r == 1 + + async def test_outer_transaction(aconn): async with aconn.transaction(): async with aconn.pipeline():