Opened 12 hours ago

Last modified 12 hours ago

#36122 new Bug

Add sanity check against updating non-composite field with composite expression

Reported by: Jacob Walls Owned by:
Component: Database layer (models, ORM) Version: 5.2
Severity: Release blocker Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Attempting to update a non-composite field via .update() with a composite expression fails at the db level with an invalid query:

Rough test:

  • tests/composite_pk/test_update.py

    diff --git a/tests/composite_pk/test_update.py b/tests/composite_pk/test_update.py
    index ec770230fc..c5deac6d4b 100644
    a b  
    11from django.db import connection
     2from django.db.models import F
    23from django.test import TestCase
    34
    45from .models import Comment, Tenant, TimeStamped, Token, User
    class CompositePKUpdateTests(TestCase):  
    175176
    176177        with self.assertRaisesMessage(ValueError, msg):
    177178            Comment.objects.update(user=User())
     179
     180    def test_update_lhs_not_composite(self):
     181        qs = Comment.objects.filter(user__email=self.user_1.email)
     182        msg = "Composite expressions only work with composite fields."
     183        with self.assertRaisesMessage(ValueError, msg):
     184            qs.update(text=F("pk"))

======================================================================
ERROR: test_update_lhs_not_composite (composite_pk.test_update.CompositePKUpdateTests.test_update_lhs_not_composite)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/source/django/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/Users/source/django/django/db/backends/sqlite3/base.py", line 360, in execute
    return super().execute(query, params)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
sqlite3.OperationalError: near ".": syntax error

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/source/django/tests/composite_pk/test_update.py", line 184, in test_update_lhs_not_composite
    qs.update(text=F("pk"))
    ~~~~~~~~~^^^^^^^^^^^^^^
  File "/Users/source/django/django/db/models/query.py", line 1254, in update
    rows = query.get_compiler(self.db).execute_sql(ROW_COUNT)
  File "/Users/source/django/django/db/models/sql/compiler.py", line 2068, in execute_sql
    row_count = super().execute_sql(result_type)
  File "/Users/source/django/django/db/models/sql/compiler.py", line 1623, in execute_sql
    cursor.execute(sql, params)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/Users/source/django/django/db/backends/utils.py", line 122, in execute
    return super().execute(sql, params)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/Users/source/django/django/db/backends/utils.py", line 79, in execute
    return self._execute_with_wrappers(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        sql, params, many=False, executor=self._execute
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/Users/source/django/django/db/backends/utils.py", line 92, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/Users/source/django/django/db/backends/utils.py", line 100, in _execute
    with self.db.wrap_database_errors:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/source/django/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/Users/source/django/django/db/backends/utils.py", line 105, in _execute
    return self.cursor.execute(sql, params)
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/Users/source/django/django/db/backends/sqlite3/base.py", line 360, in execute
    return super().execute(query, params)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
django.db.utils.OperationalError: near ".": syntax error

----------------------------------------------------------------------
(0.000) UPDATE "composite_pk_comment"
SET "text" = "composite_pk_comment"."tenant_id",
    "composite_pk_comment"."comment_id"
WHERE ("composite_pk_comment"."tenant_id",
       "composite_pk_comment"."comment_id") IN
    (SELECT U0."tenant_id" AS "tenant",
            U0."comment_id" AS "id"
     FROM "composite_pk_comment" U0
     INNER JOIN "composite_pk_user" U1 ON (U0."tenant_id" = U1."tenant_id"
                                           AND U0."user_id" = U1."id")
     WHERE U1."email" = 'user0001@example.com'); args=('user0001@example.com',); alias=default

----------------------------------------------------------------------
Ran 1 test in 0.014s

FAILED (errors=1)

Change History (1)

comment:1 by Jacob Walls, 12 hours ago

I guess depending on the fix for #36120 this may turn out to be a duplicate (pardon the noise if so)

Note: See TracTickets for help on using tickets.
Back to Top