]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Warn when trying to execute a query object with a session.
authorFederico Caselli <cfederico87@gmail.com>
Fri, 1 Oct 2021 20:16:14 +0000 (22:16 +0200)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Oct 2021 18:33:29 +0000 (14:33 -0400)
Passing a :class:`.Query` object to :meth:`_orm.Session.execute` is not
the intended use of this object, and will now raise a deprecation warning.

Fixes: #6284
Change-Id: I30a406d5a72335f9405f10f0a2030a32ccda41b9

doc/build/changelog/unreleased_14/6284.rst [new file with mode: 0644]
lib/sqlalchemy/sql/coercions.py
test/ext/test_baked.py
test/orm/test_bind.py
test/orm/test_query.py

diff --git a/doc/build/changelog/unreleased_14/6284.rst b/doc/build/changelog/unreleased_14/6284.rst
new file mode 100644 (file)
index 0000000..ffb506c
--- /dev/null
@@ -0,0 +1,6 @@
+.. change::
+    :tags: orm
+    :tickets: 6284
+
+    Passing a :class:`.Query` object to :meth:`_orm.Session.execute` is not
+    the intended use of this object, and will now raise a deprecation warning.
index dce32935245b65b980c52bd6ad2ddf6497ec3daf..f888bad4cac77c09958683ec7d1869940368380c 100644 (file)
@@ -191,7 +191,6 @@ def expect(
                 resolved = element
     else:
         resolved = element
-
     if (
         apply_propagate_attrs is not None
         and not apply_propagate_attrs._propagate_attrs
@@ -843,6 +842,28 @@ class ReturnsRowsImpl(RoleImpl):
 class StatementImpl(_CoerceLiterals, RoleImpl):
     __slots__ = ()
 
+    def _post_coercion(self, resolved, original_element, argname=None, **kw):
+        if resolved is not original_element and not isinstance(
+            original_element, util.string_types
+        ):
+            # use same method as Connection uses; this will later raise
+            # ObjectNotExecutableError
+            try:
+                original_element._execute_on_connection
+            except AttributeError:
+                util.warn_deprecated(
+                    "Object %r should not be used directly in a SQL statement "
+                    "context, such as passing to methods such as "
+                    "session.execute().  This usage will be disallowed in a "
+                    "future release.  "
+                    "Please use Core select() / update() / delete() etc. "
+                    "with Session.execute() and other statement execution "
+                    "methods." % original_element,
+                    "1.4",
+                )
+
+        return resolved
+
     def _implicit_coercions(
         self, original_element, resolved, argname=None, **kw
     ):
index 8e96dd3adb92a2dbcfab0d576c81369a08a6ec5f..ace76cff57b3f07859fa375e1057987759ead49b 100644 (file)
@@ -1080,14 +1080,14 @@ class CustomIntegrationTest(testing.AssertsCompiledSQL, BakedTest):
         q = sess.query(User).filter(User.id == 7).set_cache_key("user7")
 
         eq_(
-            sess.execute(q).all(),
+            sess.execute(q.statement).all(),
             [(User(id=7, addresses=[Address(id=1)]),)],
         )
 
         eq_(list(q.cache), ["user7"])
 
         eq_(
-            sess.execute(q).all(),
+            sess.execute(q.statement).all(),
             [(User(id=7, addresses=[Address(id=1)]),)],
         )
 
index 3df5a27177762253074b7eb191a3ae3f4169071c..4f6f4c4fbbe7a93265c7913349b4dd0b4320cbfc 100644 (file)
@@ -292,7 +292,7 @@ class BindIntegrationTest(_fixtures.FixtureTest):
 
     @testing.combinations(
         (
-            lambda session, Address: session.query(Address),
+            lambda session, Address: session.query(Address).statement,
             lambda Address: {"mapper": inspect(Address), "clause": mock.ANY},
             "e2",
         ),
index 80fb913ffcb6ba17525d877094689313ba0e712e..def2fd6791cf26490421525fa042c8628e8095a1 100644 (file)
@@ -137,6 +137,36 @@ class MiscTest(QueryTest):
             ).compare(q1.selectable)
         )
 
+    @testing.combinations(("session",), ("connection",), argnames="executor")
+    @testing.combinations(
+        ("execute",), ("scalars",), ("scalar",), argnames="method"
+    )
+    def test_no_query_in_execute(self, executor, method, connection):
+        # even though this test is testing deprecations, these deprecations
+        # become errors when removed so we dont want to remove this test,
+        # just update it
+
+        if executor == "session":
+            exec_obj = Session(connection)
+        else:
+            exec_obj = connection
+
+        meth = getattr(exec_obj, method)
+
+        q = Session().query(literal_column("1"))
+
+        if executor == "session":
+            with testing.expect_deprecated(
+                r"Object .*Query.* should not be used directly in a "
+                r"SQL statement context"
+            ):
+                meth(q)
+        else:
+            with testing.expect_raises_message(
+                sa_exc.ObjectNotExecutableError, "Not an executable object"
+            ):
+                meth(q)
+
 
 class OnlyReturnTuplesTest(QueryTest):
     def test_single_entity_false(self):
@@ -297,7 +327,10 @@ class RowTupleTest(QueryTest, AssertsCompiledSQL):
 
         q = testing.resolve_lambda(test_case, **locals())
 
-        row = s.execute(q.order_by(User.id)).first()
+        if isinstance(q, Query):
+            row = q.first()
+        else:
+            row = s.execute(q.order_by(User.id)).first()
         assert "jack" in row
 
     @testing.combinations(