--- /dev/null
+.. change::
+ :tags: orm
+ :tickets: 11163
+
+ Added a warning noting when an
+ :meth:`_engine.ConnectionEvents.engine_connect` event may be leaving
+ a transaction open, which can alter the behavior of a
+ :class:`_orm.Session` using such an engine as bind.
+ On SQLAlchemy 2.1 :paramref:`_orm.Session.join_transaction_mode` will
+ instead be ignored in all cases when the session bind is
+ an :class:`_engine.Engine`.
else:
join_transaction_mode = "rollback_only"
+ if local_connect:
+ util.warn(
+ "The engine provided as bind produced a "
+ "connection that is already in a transaction. "
+ "This is usually caused by a core event, "
+ "such as 'engine_connect', that has left a "
+ "transaction open. The effective join "
+ "transaction mode used by this session is "
+ f"{join_transaction_mode!r}. To silence this "
+ "warning, do not leave transactions open"
+ )
if join_transaction_mode in (
"control_fully",
"rollback_only",
trans.commit()
assert len(sess.query(User).all()) == 1
- @testing.variation(
+ join_transaction_mode = testing.variation(
"join_transaction_mode",
[
"none",
"rollback_only",
],
)
+
+ @join_transaction_mode
@testing.variation("operation", ["commit", "close", "rollback", "nothing"])
@testing.variation("external_state", ["none", "transaction", "savepoint"])
def test_join_transaction_modes(
else:
external_state.fail()
+ @join_transaction_mode
+ def test_join_transaction_mode_with_event(self, join_transaction_mode):
+ eng = engines.testing_engine()
+
+ @event.listens_for(eng, "engine_connect")
+ def make_transaction(conn):
+ conn.begin()
+
+ if join_transaction_mode.none:
+ s = Session(eng)
+ else:
+ s = Session(eng, join_transaction_mode=join_transaction_mode.name)
+ if (
+ join_transaction_mode.none
+ or join_transaction_mode.conditional_savepoint
+ ):
+ with expect_warnings(
+ "The engine provided as bind produced a "
+ "connection that is already in a transaction. "
+ "This is usually caused by a core event, "
+ "such as 'engine_connect', that has left a "
+ "transaction open. The effective join "
+ "transaction mode used by this session is "
+ "'rollback_only'. To silence this "
+ "warning, do not leave transactions open"
+ ):
+ s.connection()
+ else:
+ s.connection()
+
def test_subtransaction_on_external_commit(self, connection_no_trans):
users, User = self.tables.users, self.classes.User
return_value=mock.Mock(
_is_future=False,
execution_options=mock.Mock(
- return_value=mock.Mock(_is_future=False)
+ return_value=mock.Mock(
+ _is_future=False,
+ in_transaction=mock.Mock(return_value=False),
+ )
),
)
)
def test_execution_options_ignored_mid_transaction(self):
bind = mock.Mock()
- conn = mock.Mock(engine=bind)
+ conn = mock.Mock(
+ engine=bind, in_transaction=mock.Mock(return_value=False)
+ )
bind.connect = mock.Mock(return_value=conn)
sess = Session(bind=bind)
sess.execute(text("select 1"))