From: Mike Bayer Date: Tue, 18 Jan 2022 22:19:24 +0000 (-0500) Subject: reject methods as lambda SQL callables X-Git-Tag: rel_2_0_0b1~535^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e28ec27b599558b3e26ced106a972e8b4aa9e668;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git reject methods as lambda SQL callables 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. Fixes: #7032 Change-Id: If714715bd8c11557ab769ee3b1a24264b0b06acc --- diff --git a/doc/build/changelog/unreleased_14/7032.rst b/doc/build/changelog/unreleased_14/7032.rst new file mode 100644 index 0000000000..c837be4944 --- /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 d71c85d609..ae7358870f 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -6,6 +6,7 @@ # the MIT License: https://www.opensource.org/licenses/mit-license.php import collections.abc as collections_abc +import inspect import itertools import operator import types @@ -618,6 +619,10 @@ class AnalyzedCode: 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 43aed06725..96a6ecbf48 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