]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Correctly interpret None passed to query.get(); warn for empty PK values
authorlizraeli <leo2002b@yahoo.com>
Mon, 28 Oct 2019 19:33:41 +0000 (15:33 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 28 Oct 2019 20:04:59 +0000 (16:04 -0400)
A warning is emitted if a primary key value is passed to :meth:`.Query.get`
that consists of None for all primary key column positions.   Previously,
passing a single None outside of a tuple would raise a ``TypeError`` and
passing a composite None (tuple of None values) would silently pass
through.   The fix now coerces the single None into a tuple where it is
handled consistently with the other None conditions.  Thanks to Lev
Izraelit for the help with this.

Fixes #4915
Closes: #4917
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4917
Pull-request-sha: b388343c7cabeecf8c779689b78e638c23f9af40

Change-Id: Ibc6c27ccf50dfd4adbf15b6dbd393115c30d44fb
(cherry picked from commit 997f4b5f2b3b4725de0960824e95fcb1150ff215)

doc/build/changelog/unreleased_13/4915.rst [new file with mode: 0644]
lib/sqlalchemy/orm/loading.py
lib/sqlalchemy/orm/query.py
test/orm/test_query.py

diff --git a/doc/build/changelog/unreleased_13/4915.rst b/doc/build/changelog/unreleased_13/4915.rst
new file mode 100644 (file)
index 0000000..1048d06
--- /dev/null
@@ -0,0 +1,12 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 4915
+
+    A warning is emitted if a primary key value is passed to :meth:`.Query.get`
+    that consists of None for all primary key column positions.   Previously,
+    passing a single None outside of a tuple would raise a ``TypeError`` and
+    passing a composite None (tuple of None values) would silently pass
+    through.   The fix now coerces the single None into a tuple where it is
+    handled consistently with the other None conditions.  Thanks to Lev
+    Izraelit for the help with this.
+
index 78ad2eb3b9990eb0f355cbba53a337815e44686a..b9bcd034fe75a1709fe7e91bdf26a63a5832d308 100644 (file)
@@ -241,6 +241,12 @@ def load_on_pk_identity(
             )
             _get_clause = sql_util.adapt_criterion_to_null(_get_clause, nones)
 
+            if len(nones) == len(primary_key_identity):
+                util.warn(
+                    "fully NULL primary key identity cannot load any "
+                    "object.  This condition may raise an error in a future "
+                    "release."
+                )
         _get_clause = q._adapt_clause(_get_clause, True, False)
         q._criterion = _get_clause
 
index d3629690ed57876a1585ce5108ac5c9702b3e6b1..35b854e79a56400bf4ef1864bc22fc5c6c152661 100644 (file)
@@ -1044,7 +1044,9 @@ class Query(object):
 
         is_dict = isinstance(primary_key_identity, dict)
         if not is_dict:
-            primary_key_identity = util.to_list(primary_key_identity)
+            primary_key_identity = util.to_list(
+                primary_key_identity, default=(None,)
+            )
 
         if len(primary_key_identity) != len(mapper.primary_key):
             raise sa_exc.InvalidRequestError(
index 3cab1169f543f51a0b28b1a8906fad3535f1b743..4e32d9c60fc37262090b446ed75ed314435b67ec 100644 (file)
@@ -726,10 +726,8 @@ class GetTest(QueryTest):
         q = s.query(User.id)
         assert_raises(sa_exc.InvalidRequestError, q.get, (5,))
 
-    def test_get_null_pk(self):
-        """test that a mapping which can have None in a
-        PK (i.e. map to an outerjoin) works with get()."""
-
+    @testing.fixture
+    def outerjoin_mapping(self):
         users, addresses = self.tables.users, self.tables.addresses
 
         s = users.outerjoin(addresses)
@@ -745,10 +743,44 @@ class GetTest(QueryTest):
                 "address_id": addresses.c.id,
             },
         )
+        return UserThing
+
+    def test_get_null_pk(self, outerjoin_mapping):
+        """test that a mapping which can have None in a
+        PK (i.e. map to an outerjoin) works with get()."""
+
+        UserThing = outerjoin_mapping
         sess = create_session()
         u10 = sess.query(UserThing).get((10, None))
         eq_(u10, UserThing(id=10))
 
+    def test_get_fully_null_pk(self):
+        User = self.classes.User
+
+        s = Session()
+        q = s.query(User)
+        assert_raises_message(
+            sa_exc.SAWarning,
+            r"fully NULL primary key identity cannot load any object.  "
+            "This condition may raise an error in a future release.",
+            q.get,
+            None,
+        )
+
+    def test_get_fully_null_composite_pk(self, outerjoin_mapping):
+        UserThing = outerjoin_mapping
+
+        s = Session()
+        q = s.query(UserThing)
+
+        assert_raises_message(
+            sa_exc.SAWarning,
+            r"fully NULL primary key identity cannot load any object.  "
+            "This condition may raise an error in a future release.",
+            q.get,
+            (None, None),
+        )
+
     def test_no_criterion(self):
         """test that get()/load() does not use preexisting filter/etc.
         criterion"""