From 963a6f4f5584d941c63d5b8d85b79f34c1e4ebb1 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 9 May 2022 15:23:51 +0200 Subject: [PATCH] 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. --- psycopg/psycopg/transaction.py | 10 ++++++++++ tests/test_pipeline.py | 10 ++++++++++ tests/test_pipeline_async.py | 10 ++++++++++ 3 files changed, 30 insertions(+) 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(): -- 2.47.2