From: Mike Bayer Date: Wed, 3 Dec 2025 04:10:10 +0000 (-0500) Subject: send same sub_stmt to after_cursor_execute as before X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6924f6c5d91be83dd646c3290d53c98afeca4d37;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git send same sub_stmt to after_cursor_execute as before Fixed issue in the :meth:`.ConnectionEvents.after_cursor_execute` method where the SQL statement and parameter list for an "insertmanyvalues" operation sent to the event would not be the actual SQL / parameters just emitted on the cursor, instead being the non-batched form of the statement that's used as a template to generate the batched statements. Fixes: #13018 Change-Id: Ib5b6c576f2c7a9ed275de30290d619cc651b4816 --- diff --git a/doc/build/changelog/unreleased_21/13018.rst b/doc/build/changelog/unreleased_21/13018.rst new file mode 100644 index 0000000000..630ab24c51 --- /dev/null +++ b/doc/build/changelog/unreleased_21/13018.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, engine + :tickets: 13018 + + Fixed issue in the :meth:`.ConnectionEvents.after_cursor_execute` method + where the SQL statement and parameter list for an "insertmanyvalues" + operation sent to the event would not be the actual SQL / parameters just + emitted on the cursor, instead being the non-batched form of the statement + that's used as a template to generate the batched statements. diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 4e2d6d878d..4e81ab478c 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -1984,13 +1984,6 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]): else: do_execute_dispatch = () - if engine_events: - _WORKAROUND_ISSUE_13018 = getattr( - self, "_WORKAROUND_ISSUE_13018", False - ) - else: - _WORKAROUND_ISSUE_13018 = False - if self._echo: stats = context._get_cache_stats() + " (insertmanyvalues)" @@ -2105,9 +2098,8 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]): self.dispatch.after_cursor_execute( self, cursor, - # TODO: this will be fixed by #13018 - sub_stmt if _WORKAROUND_ISSUE_13018 else str_statement, - sub_params if _WORKAROUND_ISSUE_13018 else parameters, + sub_stmt, + sub_params, context, context.executemany, ) diff --git a/lib/sqlalchemy/testing/assertsql.py b/lib/sqlalchemy/testing/assertsql.py index 603955a8b5..db28709cc3 100644 --- a/lib/sqlalchemy/testing/assertsql.py +++ b/lib/sqlalchemy/testing/assertsql.py @@ -484,7 +484,6 @@ def assert_engine(engine): def connection_execute( conn, clauseelement, multiparams, params, execution_options ): - conn._WORKAROUND_ISSUE_13018 = True # grab the original statement + params before any cursor # execution orig[:] = clauseelement, multiparams, params diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index b07c0cc9a8..600cf10da9 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -912,6 +912,38 @@ class ExecuteDriverTest(fixtures.TablesTest): eq_(conn.scalar(select(1)), 1) eng.dispose() + @testing.requires.insertmanyvalues + def test_cursor_execute_insertmanyvalues(self, connection, metadata): + """test #13018, that before_cursor_execute and after_cursor_execute + get the inner INSERT statements / params for an insertmanyvalues + + """ + canary = Mock() + + t = Table( + "t", + metadata, + Column("id", Integer, primary_key=True), + Column("data", String(50)), + ) + t.create(connection) + + event.listen(connection, "before_cursor_execute", canary.bce) + event.listen(connection, "after_cursor_execute", canary.ace) + + result = connection.execute( + t.insert().returning( + t.c.id, t.c.data, sort_by_parameter_order=True + ), + [{"data": f"d{i}"} for i in range(10)], + ) + eq_(result.all(), [(i + 1, f"d{i}") for i in range(10)]) + + eq_( + [(c1.args[2], c1.args[3]) for c1 in canary.bce.mock_calls], + [(c1.args[2], c1.args[3]) for c1 in canary.ace.mock_calls], + ) + class CompiledCacheTest(fixtures.TestBase): __sparse_driver_backend__ = True