summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/util.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-12-01 19:27:25 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2021-12-06 13:05:44 -0500
commita845da8b0fc5bb172e278c399a1de9a2e49d62af (patch)
treeca86fe60e4e69fdef15cb9593d57d9042678700f /lib/sqlalchemy/engine/util.py
parent55e0497b080bf7f5159faa5abcb341269ebfdc7f (diff)
downloadsqlalchemy-a845da8b0fc5bb172e278c399a1de9a2e49d62af.tar.gz
contextmanager skips rollback if trans says to skip it
Fixed issue where if an exception occurred when the :class:`_orm.Session` were to close the connection within the :meth:`_orm.Session.commit` method, when using a context manager for :meth:`_orm.Session.begin` , it would attempt a rollback which would not be possible as the :class:`_orm.Session` was in between where the transaction is committed and the connection is then to be returned to the pool, raising the exception "this sessiontransaction is in the committed state". This exception can occur mostly in an asyncio context where CancelledError can be raised. Fixes: #7388 Change-Id: I1a85a3a7eae79f3553ddf1e3d245a0d90b0a2f40
Diffstat (limited to 'lib/sqlalchemy/engine/util.py')
-rw-r--r--lib/sqlalchemy/engine/util.py23
1 files changed, 21 insertions, 2 deletions
diff --git a/lib/sqlalchemy/engine/util.py b/lib/sqlalchemy/engine/util.py
index 732ae7fa1..e88b9ebf3 100644
--- a/lib/sqlalchemy/engine/util.py
+++ b/lib/sqlalchemy/engine/util.py
@@ -98,6 +98,23 @@ class TransactionalContext:
def _transaction_is_closed(self):
raise NotImplementedError()
+ def _rollback_can_be_called(self):
+ """indicates the object is in a state that is known to be acceptable
+ for rollback() to be called.
+
+ This does not necessarily mean rollback() will succeed or not raise
+ an error, just that there is currently no state detected that indicates
+ rollback() would fail or emit warnings.
+
+ It also does not mean that there's a transaction in progress, as
+ it is usually safe to call rollback() even if no transaction is
+ present.
+
+ .. versionadded:: 1.4.28
+
+ """
+ raise NotImplementedError()
+
def _get_subject(self):
raise NotImplementedError()
@@ -143,7 +160,8 @@ class TransactionalContext:
self.commit()
except:
with util.safe_reraise():
- self.rollback()
+ if self._rollback_can_be_called():
+ self.rollback()
finally:
if not out_of_band_exit:
subject._trans_context_manager = self._outer_trans_ctx
@@ -154,7 +172,8 @@ class TransactionalContext:
if not self._transaction_is_closed():
self.close()
else:
- self.rollback()
+ if self._rollback_can_be_called():
+ self.rollback()
finally:
if not out_of_band_exit:
subject._trans_context_manager = self._outer_trans_ctx