diff options
| -rw-r--r-- | doc/build/changelog/unreleased_14/7032.rst | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/lambdas.py | 5 | ||||
| -rw-r--r-- | test/sql/test_lambdas.py | 21 |
3 files changed, 36 insertions, 0 deletions
diff --git a/doc/build/changelog/unreleased_14/7032.rst b/doc/build/changelog/unreleased_14/7032.rst new file mode 100644 index 000000000..c837be494 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7032.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, sql + :tickets: 7032 + + Added an informative error message when a method object is passed to a SQL + construct. Previously, when such a callable were passed, as is a common + typographical error when dealing with method-chained SQL constructs, they + were interpreted as "lambda SQL" targets to be invoked at compilation time, + which would lead to silent failures. As this feature was not intended to be + used with methods, method objects are now rejected. diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index e22f87167..5f9155998 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -5,6 +5,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: https://www.opensource.org/licenses/mit-license.php +import inspect import itertools import operator import sys @@ -619,6 +620,10 @@ class AnalyzedCode(object): return analyzed def __init__(self, fn, lambda_element, opts): + if inspect.ismethod(fn): + raise exc.ArgumentError( + "Method %s may not be passed as a SQL expression" % fn + ) closure = fn.__closure__ self.track_bound_values = ( diff --git a/test/sql/test_lambdas.py b/test/sql/test_lambdas.py index 76be0af3c..29e1258ef 100644 --- a/test/sql/test_lambdas.py +++ b/test/sql/test_lambdas.py @@ -8,6 +8,7 @@ from sqlalchemy.sql import and_ from sqlalchemy.sql import bindparam from sqlalchemy.sql import coercions from sqlalchemy.sql import column +from sqlalchemy.sql import func from sqlalchemy.sql import join from sqlalchemy.sql import lambda_stmt from sqlalchemy.sql import lambdas @@ -38,6 +39,26 @@ class LambdaElementTest( ): __dialect__ = "default" + def test_reject_methods(self): + """test #7032""" + + t1 = table("t1", column("q"), column("p")) + + subq = select(t1).subquery + + with expect_raises_message( + exc.ArgumentError, + "Method <bound method Select.*.subquery .* may not be " + "passed as a SQL expression", + ): + select(func.count()).select_from(subq) + + self.assert_compile( + select(func.count()).select_from(subq()), + "SELECT count(*) AS count_1 FROM " + "(SELECT t1.q AS q, t1.p AS p FROM t1) AS anon_1", + ) + def test_select_whereclause(self): t1 = table("t1", column("q"), column("p")) |
