#30379: ProgrammingError from exclude using models.F with long relation
-------------------------------------+-------------------------------------
     Reporter:  PeterJCLaw           |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  2.2
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by PeterJCLaw:

Old description:

> The following query emits a `ProgrammingError` due to the query omitting
> the definitions of a number of references it uses:
> {{{#!python
> Alert.objects.exclude(
>     item__order_items__order__user=models.F('user'),
> )
> }}}
>
> {{{
> django.db.utils.ProgrammingError: missing FROM-clause entry for table
> "u2"
> }}}
>
> The spelling using a specific user does seem to work correctly, hinting
> that the issue is somehow related to the handling of the `models.F`
> component.
>
> {{{#!python
> Alert.objects.exclude(
>     item__order_items__order__user=the_user,
> )
> }}}
>
> Models:
> {{{#!python
> from django.db import models
>
> class Item(models.Model):
>     name = models.TextField()
>
> class Order(models.Model):
>     user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
>
> class UnsizedOrderItem(models.Model):
>     order = models.ForeignKey(Order, on_delete=models.CASCADE,
> related_name='items')
>     item = models.ForeignKey(Item, on_delete=models.CASCADE,
> related_name='order_items')
>
> class Alert(models.Model):
>     user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
>     item = models.ForeignKey(Item, on_delete=models.CASCADE,
> related_name='alerts')
> }}}
>
> I originally saw this with another layer of many-to-many between the item
> and the order, but that turned out not to be necessary to reproduce the
> issue:
> {{{#!python
> class SizedItem(models.Model):
>     item = models.ForeignKey(Item, on_delete=models.CASCADE,
> related_name='sizes')
>     size = models.TextField()
>
> class SizedOrderItem(models.Model):
>     order = models.ForeignKey(Order, on_delete=models.CASCADE,
> related_name='sized_items')
>     sized_item = models.ForeignKey(SizedItem, on_delete=models.CASCADE,
> related_name='sized_order_items')
>
> Alert.objects.exclude(
>     item__sizes__sized_order_items__order__user=models.F('user'),
> )
> }}}
>
> Traceback:
> {{{
>   File ".../lib/python3.5/site-packages/django/db/models/query.py", line
> 55, in __iter__
>     results = compiler.execute_sql(chunked_fetch=self.chunked_fetch,
> chunk_size=self.chunk_size)
>   File ".../lib/python3.5/site-
> packages/django/db/models/sql/compiler.py", line 1097, in execute_sql
>     cursor.execute(sql, params)
>   File ".../lib/python3.5/site-
> packages/sentry_sdk/integrations/django/__init__.py", line 298, in
> execute
>     return real_execute(self, sql, params)
>   File "/home/peter-thread/thread/styleme/styleme/graphql/metrics.py",
> line 112, in wrapper
>     return fn(wrapped_self, *args, **kwargs)
>   File ".../lib/python3.5/site-packages/django/db/backends/utils.py",
> line 67, in execute
>     return self._execute_with_wrappers(sql, params, many=False,
> executor=self._execute)
>   File ".../lib/python3.5/site-packages/django/db/backends/utils.py",
> line 76, in _execute_with_wrappers
>     return executor(sql, params, many, context)
>   File ".../lib/python3.5/site-packages/django/db/backends/utils.py",
> line 84, in _execute
>     return self.cursor.execute(sql, params)
>   File ".../lib/python3.5/site-packages/django/db/utils.py", line 89, in
> __exit__
>     raise dj_exc_value.with_traceback(traceback) from exc_value
>   File ".../lib/python3.5/site-packages/django/db/backends/utils.py",
> line 84, in _execute
>     return self.cursor.execute(sql, params)
> django.db.utils.ProgrammingError: missing FROM-clause entry for table
> "u2"
> LINE 1: ...JOIN "liked_email_alerts_unsizedorderitem" U3 ON (U2."id" =
> ...
>                                                              ^
> }}}
>
> Versions:
>  * I've seen this under both 2.1.7 and 2.2
>  * I'm using Python 3.5.2
>  * I'm using Postgres 9.6 (psycopg2-binary==2.7.7)
>
> For reference here's the SQL generated by printing the `.query` on the
> querysets:
> {{{#!sql
> -- relative
>
> SELECT
>     "liked_email_alerts_alert"."id",
>     "liked_email_alerts_alert"."user_id",
>     "liked_email_alerts_alert"."item_id"
> FROM
>     "liked_email_alerts_alert"
> WHERE
>     NOT ("liked_email_alerts_alert"."item_id" IN (
>             SELECT
>                 U3. "item_id"
>             FROM
>                 "liked_email_alerts_alert" U0
>                 INNER JOIN "liked_email_alerts_unsizedorderitem" U3 ON
> (U2. "id" = U3. "item_id")
>                 INNER JOIN "liked_email_alerts_order" U4 ON (U3.
> "order_id" = U4. "id")
>             WHERE
>                 U4. "user_id" = (U0. "user_id")));
>
> -- specific
>
> SELECT
>     "liked_email_alerts_alert"."id",
>     "liked_email_alerts_alert"."user_id",
>     "liked_email_alerts_alert"."item_id"
> FROM
>     "liked_email_alerts_alert"
> WHERE
>     NOT ("liked_email_alerts_alert"."item_id" IN (
>             SELECT
>                 U2. "item_id"
>             FROM
>                 "liked_email_alerts_unsizedorderitem" U2
>                 INNER JOIN "liked_email_alerts_order" U3 ON (U2.
> "order_id" = U3. "id")
>             WHERE
>                 U3. "user_id" = 13))
> }}}

New description:

 The following query emits a `ProgrammingError` due to the query omitting
 the definitions of a number of references it uses:
 {{{#!python
 Alert.objects.exclude(
     item__order_items__order__user=models.F('user'),
 )
 }}}

 {{{
 django.db.utils.ProgrammingError: missing FROM-clause entry for table "u2"
 }}}

 The spelling using a specific user does seem to work correctly, hinting
 that the issue is somehow related to the handling of the `models.F`
 component.

 {{{#!python
 Alert.objects.exclude(
     item__order_items__order__user=the_user,
 )
 }}}

 Models:
 {{{#!python
 from django.db import models

 class Item(models.Model):
     name = models.TextField()

 class Order(models.Model):
     user = models.ForeignKey('auth.User', on_delete=models.CASCADE)

 class UnsizedOrderItem(models.Model):
     order = models.ForeignKey(Order, on_delete=models.CASCADE,
 related_name='items')
     item = models.ForeignKey(Item, on_delete=models.CASCADE,
 related_name='order_items')

 class Alert(models.Model):
     user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
     item = models.ForeignKey(Item, on_delete=models.CASCADE,
 related_name='alerts')
 }}}

 I originally saw this with another layer of many-to-many between the item
 and the order, but that turned out not to be necessary to reproduce the
 issue:
 {{{#!python
 class SizedItem(models.Model):
     item = models.ForeignKey(Item, on_delete=models.CASCADE,
 related_name='sizes')
     size = models.TextField()

 class SizedOrderItem(models.Model):
     order = models.ForeignKey(Order, on_delete=models.CASCADE,
 related_name='sized_items')
     sized_item = models.ForeignKey(SizedItem, on_delete=models.CASCADE,
 related_name='sized_order_items')

 Alert.objects.exclude(
     item__sizes__sized_order_items__order__user=models.F('user'),
 )
 }}}

 Traceback:
 {{{
   File ".../lib/python3.5/site-packages/django/db/models/query.py", line
 55, in __iter__
     results = compiler.execute_sql(chunked_fetch=self.chunked_fetch,
 chunk_size=self.chunk_size)
   File ".../lib/python3.5/site-packages/django/db/models/sql/compiler.py",
 line 1097, in execute_sql
     cursor.execute(sql, params)
   File ".../lib/python3.5/site-
 packages/sentry_sdk/integrations/django/__init__.py", line 298, in execute
     return real_execute(self, sql, params)
   File ".../lib/python3.5/site-packages/django/db/backends/utils.py", line
 67, in execute
     return self._execute_with_wrappers(sql, params, many=False,
 executor=self._execute)
   File ".../lib/python3.5/site-packages/django/db/backends/utils.py", line
 76, in _execute_with_wrappers
     return executor(sql, params, many, context)
   File ".../lib/python3.5/site-packages/django/db/backends/utils.py", line
 84, in _execute
     return self.cursor.execute(sql, params)
   File ".../lib/python3.5/site-packages/django/db/utils.py", line 89, in
 __exit__
     raise dj_exc_value.with_traceback(traceback) from exc_value
   File ".../lib/python3.5/site-packages/django/db/backends/utils.py", line
 84, in _execute
     return self.cursor.execute(sql, params)
 django.db.utils.ProgrammingError: missing FROM-clause entry for table "u2"
 LINE 1: ...JOIN "liked_email_alerts_unsizedorderitem" U3 ON (U2."id" = ...
                                                              ^
 }}}

 Versions:
  * I've seen this under both 2.1.7 and 2.2
  * I'm using Python 3.5.2
  * I'm using Postgres 9.6 (psycopg2-binary==2.7.7)

 For reference here's the SQL generated by printing the `.query` on the
 querysets:
 {{{#!sql
 -- relative

 SELECT
     "liked_email_alerts_alert"."id",
     "liked_email_alerts_alert"."user_id",
     "liked_email_alerts_alert"."item_id"
 FROM
     "liked_email_alerts_alert"
 WHERE
     NOT ("liked_email_alerts_alert"."item_id" IN (
             SELECT
                 U3. "item_id"
             FROM
                 "liked_email_alerts_alert" U0
                 INNER JOIN "liked_email_alerts_unsizedorderitem" U3 ON
 (U2. "id" = U3. "item_id")
                 INNER JOIN "liked_email_alerts_order" U4 ON (U3.
 "order_id" = U4. "id")
             WHERE
                 U4. "user_id" = (U0. "user_id")));

 -- specific

 SELECT
     "liked_email_alerts_alert"."id",
     "liked_email_alerts_alert"."user_id",
     "liked_email_alerts_alert"."item_id"
 FROM
     "liked_email_alerts_alert"
 WHERE
     NOT ("liked_email_alerts_alert"."item_id" IN (
             SELECT
                 U2. "item_id"
             FROM
                 "liked_email_alerts_unsizedorderitem" U2
                 INNER JOIN "liked_email_alerts_order" U3 ON (U2.
 "order_id" = U3. "id")
             WHERE
                 U3. "user_id" = 13))
 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30379#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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/068.64366c77618c37e39a34a2e956f1a00e%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to