Skip to content

Commit b5822c2

Browse files
zzzeekGerrit Code Review
authored andcommitted
Merge "restore statement substitution to before_execute()"
2 parents 2900c47 + f9ba830 commit b5822c2

4 files changed

Lines changed: 77 additions & 1 deletion

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.. change::
2+
:tags: bug, engine, regression
3+
:tickets: 6913
4+
5+
Fixed issue where the ability of the
6+
:meth:`_engine.ConnectionEvents.before_execute` method to alter the SQL
7+
statement object passed, returning the new object to be invoked, was
8+
inadvertently removed. This behavior has been restored.
9+

lib/sqlalchemy/engine/base.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,7 @@ def _execute_default(
12871287

12881288
if self._has_events or self.engine._has_events:
12891289
(
1290+
default,
12901291
distilled_params,
12911292
event_multiparams,
12921293
event_params,
@@ -1335,6 +1336,7 @@ def _execute_ddl(self, ddl, multiparams, params, execution_options):
13351336

13361337
if self._has_events or self.engine._has_events:
13371338
(
1339+
ddl,
13381340
distilled_params,
13391341
event_multiparams,
13401342
event_params,
@@ -1399,7 +1401,7 @@ def _invoke_before_exec_event(
13991401
else:
14001402
distilled_params = []
14011403

1402-
return distilled_params, event_multiparams, event_params
1404+
return elem, distilled_params, event_multiparams, event_params
14031405

14041406
def _execute_clauseelement(
14051407
self, elem, multiparams, params, execution_options
@@ -1415,6 +1417,7 @@ def _execute_clauseelement(
14151417
has_events = self._has_events or self.engine._has_events
14161418
if has_events:
14171419
(
1420+
elem,
14181421
distilled_params,
14191422
event_multiparams,
14201423
event_params,
@@ -1492,6 +1495,7 @@ def _execute_compiled(
14921495

14931496
if self._has_events or self.engine._has_events:
14941497
(
1498+
compiled,
14951499
distilled_params,
14961500
event_multiparams,
14971501
event_params,
@@ -1536,6 +1540,7 @@ def _exec_driver_sql(
15361540
if not future:
15371541
if self._has_events or self.engine._has_events:
15381542
(
1543+
statement,
15391544
distilled_params,
15401545
event_multiparams,
15411546
event_params,

test/engine/test_deprecations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,17 @@ def after_execute(
16871687
)
16881688
eq_(result.all(), [("15",)])
16891689

1690+
@testing.only_on("sqlite")
1691+
def test_modify_statement_string(self, connection):
1692+
@event.listens_for(connection, "before_execute", retval=True)
1693+
def _modify(
1694+
conn, clauseelement, multiparams, params, execution_options
1695+
):
1696+
return clauseelement.replace("hi", "there"), multiparams, params
1697+
1698+
with _string_deprecation_expect():
1699+
eq_(connection.scalar("select 'hi'"), "there")
1700+
16901701
def test_retval_flag(self):
16911702
canary = []
16921703

test/engine/test_execute.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from sqlalchemy.pool import QueuePool
3232
from sqlalchemy.sql import column
3333
from sqlalchemy.sql import literal
34+
from sqlalchemy.sql.elements import literal_column
3435
from sqlalchemy.testing import assert_raises
3536
from sqlalchemy.testing import assert_raises_message
3637
from sqlalchemy.testing import config
@@ -1771,6 +1772,56 @@ def before_execute(
17711772
with e1.connect() as conn:
17721773
conn.execute(select(literal("1")))
17731774

1775+
@testing.only_on("sqlite")
1776+
def test_dont_modify_statement_driversql(self, connection):
1777+
m1 = mock.Mock()
1778+
1779+
@event.listens_for(connection, "before_execute", retval=True)
1780+
def _modify(
1781+
conn, clauseelement, multiparams, params, execution_options
1782+
):
1783+
m1.run_event()
1784+
return clauseelement.replace("hi", "there"), multiparams, params
1785+
1786+
# the event does not take effect for the "driver SQL" option
1787+
eq_(connection.exec_driver_sql("select 'hi'").scalar(), "hi")
1788+
1789+
# event is not called at all
1790+
eq_(m1.mock_calls, [])
1791+
1792+
@testing.combinations((True,), (False,), argnames="future")
1793+
@testing.only_on("sqlite")
1794+
def test_modify_statement_internal_driversql(self, connection, future):
1795+
m1 = mock.Mock()
1796+
1797+
@event.listens_for(connection, "before_execute", retval=True)
1798+
def _modify(
1799+
conn, clauseelement, multiparams, params, execution_options
1800+
):
1801+
m1.run_event()
1802+
return clauseelement.replace("hi", "there"), multiparams, params
1803+
1804+
eq_(
1805+
connection._exec_driver_sql(
1806+
"select 'hi'", [], {}, {}, future=future
1807+
).scalar(),
1808+
"hi" if future else "there",
1809+
)
1810+
1811+
if future:
1812+
eq_(m1.mock_calls, [])
1813+
else:
1814+
eq_(m1.mock_calls, [call.run_event()])
1815+
1816+
def test_modify_statement_clauseelement(self, connection):
1817+
@event.listens_for(connection, "before_execute", retval=True)
1818+
def _modify(
1819+
conn, clauseelement, multiparams, params, execution_options
1820+
):
1821+
return select(literal_column("'there'")), multiparams, params
1822+
1823+
eq_(connection.scalar(select(literal_column("'hi'"))), "there")
1824+
17741825
def test_argument_format_execute(self, testing_engine):
17751826
def before_execute(
17761827
conn, clauseelement, multiparams, params, execution_options

0 commit comments

Comments
 (0)