diff options
author | mike bayer <mike_mp@zzzcomputing.com> | 2021-08-23 16:37:10 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-08-23 16:37:10 +0000 |
commit | b5822c2d4a1044d72a622a36bfda71a7c8c08caf (patch) | |
tree | e0ab77c43a269ee5beb0a7b783df58f064c2fdd6 | |
parent | 2900c472d7f8975e29967048da6228024e098c5e (diff) | |
parent | f9ba830ec059f3a4b095c36f94fe7ae29ba7aac2 (diff) | |
download | sqlalchemy-b5822c2d4a1044d72a622a36bfda71a7c8c08caf.tar.gz |
Merge "restore statement substitution to before_execute()"
-rw-r--r-- | doc/build/changelog/unreleased_14/6913.rst | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 7 | ||||
-rw-r--r-- | test/engine/test_deprecations.py | 11 | ||||
-rw-r--r-- | test/engine/test_execute.py | 51 |
4 files changed, 77 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_14/6913.rst b/doc/build/changelog/unreleased_14/6913.rst new file mode 100644 index 000000000..43ce34c21 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6913.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, engine, regression + :tickets: 6913 + + Fixed issue where the ability of the + :meth:`_engine.ConnectionEvents.before_execute` method to alter the SQL + statement object passed, returning the new object to be invoked, was + inadvertently removed. This behavior has been restored. + diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index c26d9a0a7..a316f904f 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -1287,6 +1287,7 @@ class Connection(Connectable): if self._has_events or self.engine._has_events: ( + default, distilled_params, event_multiparams, event_params, @@ -1335,6 +1336,7 @@ class Connection(Connectable): if self._has_events or self.engine._has_events: ( + ddl, distilled_params, event_multiparams, event_params, @@ -1399,7 +1401,7 @@ class Connection(Connectable): else: distilled_params = [] - return distilled_params, event_multiparams, event_params + return elem, distilled_params, event_multiparams, event_params def _execute_clauseelement( self, elem, multiparams, params, execution_options @@ -1415,6 +1417,7 @@ class Connection(Connectable): has_events = self._has_events or self.engine._has_events if has_events: ( + elem, distilled_params, event_multiparams, event_params, @@ -1492,6 +1495,7 @@ class Connection(Connectable): if self._has_events or self.engine._has_events: ( + compiled, distilled_params, event_multiparams, event_params, @@ -1536,6 +1540,7 @@ class Connection(Connectable): if not future: if self._has_events or self.engine._has_events: ( + statement, distilled_params, event_multiparams, event_params, diff --git a/test/engine/test_deprecations.py b/test/engine/test_deprecations.py index 795cc5a4c..39e2bf762 100644 --- a/test/engine/test_deprecations.py +++ b/test/engine/test_deprecations.py @@ -1687,6 +1687,17 @@ class EngineEventsTest(fixtures.TestBase): ) eq_(result.all(), [("15",)]) + @testing.only_on("sqlite") + def test_modify_statement_string(self, connection): + @event.listens_for(connection, "before_execute", retval=True) + def _modify( + conn, clauseelement, multiparams, params, execution_options + ): + return clauseelement.replace("hi", "there"), multiparams, params + + with _string_deprecation_expect(): + eq_(connection.scalar("select 'hi'"), "there") + def test_retval_flag(self): canary = [] diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 19ba5f03c..dd4ee32f8 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -31,6 +31,7 @@ from sqlalchemy.pool import NullPool from sqlalchemy.pool import QueuePool from sqlalchemy.sql import column from sqlalchemy.sql import literal +from sqlalchemy.sql.elements import literal_column from sqlalchemy.testing import assert_raises from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import config @@ -1771,6 +1772,56 @@ class EngineEventsTest(fixtures.TestBase): with e1.connect() as conn: conn.execute(select(literal("1"))) + @testing.only_on("sqlite") + def test_dont_modify_statement_driversql(self, connection): + m1 = mock.Mock() + + @event.listens_for(connection, "before_execute", retval=True) + def _modify( + conn, clauseelement, multiparams, params, execution_options + ): + m1.run_event() + return clauseelement.replace("hi", "there"), multiparams, params + + # the event does not take effect for the "driver SQL" option + eq_(connection.exec_driver_sql("select 'hi'").scalar(), "hi") + + # event is not called at all + eq_(m1.mock_calls, []) + + @testing.combinations((True,), (False,), argnames="future") + @testing.only_on("sqlite") + def test_modify_statement_internal_driversql(self, connection, future): + m1 = mock.Mock() + + @event.listens_for(connection, "before_execute", retval=True) + def _modify( + conn, clauseelement, multiparams, params, execution_options + ): + m1.run_event() + return clauseelement.replace("hi", "there"), multiparams, params + + eq_( + connection._exec_driver_sql( + "select 'hi'", [], {}, {}, future=future + ).scalar(), + "hi" if future else "there", + ) + + if future: + eq_(m1.mock_calls, []) + else: + eq_(m1.mock_calls, [call.run_event()]) + + def test_modify_statement_clauseelement(self, connection): + @event.listens_for(connection, "before_execute", retval=True) + def _modify( + conn, clauseelement, multiparams, params, execution_options + ): + return select(literal_column("'there'")), multiparams, params + + eq_(connection.scalar(select(literal_column("'hi'"))), "there") + def test_argument_format_execute(self, testing_engine): def before_execute( conn, clauseelement, multiparams, params, execution_options |