--- /dev/null
+.. 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.
# 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
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 = (
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
):
__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 SelectBase.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"))