#36213: Add warning to documentation: QuerySet.update can execute two separate 
SQL
queries when using MySQL
-------------------------------------+-------------------------------------
     Reporter:  Babak                |                    Owner:  (none)
         Type:  Uncategorized        |                   Status:  new
    Component:  Documentation        |                  Version:
     Severity:  Normal               |               Resolution:
     Keywords:  mysql self-select    |             Triage Stage:
  race-condition                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Babak:

Old description:

> When you have a QuerySet that has filter conditions based on related
> tables, the `QuerySet.update()` function will execute two separate SQL
> queries (a `SELECT`, followed by an `UPDATE`).
>
> Examples:
>
> `BlogPost.objects.filter(published=True).update(foo="bar")` (Causes a
> single `UPDATE`)
>
> `BlogPost.objects.filter(published=True, author__name="Foo
> Bar").update(foo="bar")` (Causes `SELECT` THEN `UPDATE`)
>
> As I was told in the [https://forum.djangoproject.com/t/queryset-update-
> silently-turns-into-select-update-mysql/39095/2 forum] (thanks
> charettes), this is an undocumented
> [https://github.com/django/django/blob/240421c7c4c81fe5df26274b807266bd4ca73d7f/django/db/backends/mysql/features.py#L165-L167
> MySQL-only behaviour] because MySQL [https://dbfiddle.uk/Zfz_e9Kw doesn't
> allow self-select updates].
>
> This will **silently cause nasty race conditions** since the update is no
> longer running as a single SQL statement.
>
> Currently the docs for `QuerySet.update` say:
>
>    Using update() also prevents a race condition wherein something might
> change in your database in the short period of time between loading the
> object and calling save().
>
> But in the case that I described, it causes the exact same type of race
> condition that the docs suggest that it prevents.
>
> If the users are aware of this behaviour they can take care to avoid such
> filter conditions or to use alternative transaction handling mechanisms
> to ensure atomic behaviour.
>
> I'd like to suggest for a warning to be added to the documentation about
> this.

New description:

 When you have a QuerySet that has filter conditions based on related
 tables, the `QuerySet.update()` function will execute two separate SQL
 queries (a `SELECT`, followed by an `UPDATE`).

 Examples:

 `BlogPost.objects.filter(published=True).update(foo="bar")` (Causes a
 single `UPDATE`)

 `BlogPost.objects.filter(published=True, author__name="Foo
 Bar").update(foo="bar")` (Causes `SELECT` THEN `UPDATE`)

 As I was told in the [https://forum.djangoproject.com/t/queryset-update-
 silently-turns-into-select-update-mysql/39095/2 forum] (thanks charettes),
 this is an undocumented
 
[https://github.com/django/django/blob/240421c7c4c81fe5df26274b807266bd4ca73d7f/django/db/backends/mysql/features.py#L165-L167
 MySQL-only behaviour] because MySQL [https://dbfiddle.uk/Zfz_e9Kw doesn't
 allow self-select updates].

 This will **silently cause nasty race conditions** since the update is no
 longer running as a single SQL statement.

 Currently the
 
[https://docs.djangoproject.com/en/5.1/ref/models/querysets/#django.db.models.query.QuerySet.update
 docs] for `QuerySet.update` say:

    Using update() also prevents a race condition wherein something might
 change in your database in the short period of time between loading the
 object and calling save().

 But in the case that I described, it causes the exact same type of race
 condition that the docs suggest that it prevents.

 If the users are aware of this behaviour they can take care to avoid such
 filter conditions or to use alternative transaction handling mechanisms to
 ensure atomic behaviour.

 I'd like to suggest for a warning to be added to the documentation about
 this.

--
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36213#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/010701953ab16efe-54c7710c-4037-4aff-a1f6-0ebe0ba80d37-000000%40eu-central-1.amazonses.com.

Reply via email to