#28199: `Subquery` in __in filter generating invalid SQL
-------------------------------------+-------------------------------------
               Reporter:  Murray     |          Owner:  nobody
  Christopherson                     |
                   Type:  Bug        |         Status:  new
              Component:             |        Version:  1.11
  Uncategorized                      |
               Severity:  Normal     |       Keywords:  subquery
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 Duplicated from https://groups.google.com/forum/#!topic/django-
 users/aq7mL9Opd-s

 Within my app, there is the concept of permits and suspensions:
 {{{#!python
 class Permit(models.Model):
     class Meta:
        db_table = 'permits'

     uuid = models.UUIDField(primary_key=True, db_column='uuid')

 class Suspension(models.Model):
     class Meta:
         db_table = 'suspensions'

     uuid = models.UUIDField(primary_key=True, db_column='uuid')
     permit = models.ForeignKey(Permit, db_column='permits_uuid')
     startDate = models.DateField(db_column='start_date')
     endDate = models.DateField(null=True, db_column='end_date')
 }}}

 Within the app, I am try to get a set of permits that are not currently
 expired, so I attempted to generate the query like this.
 {{{#!python
 activeSuspensionPermitUuidsQuery =
 Suspension.objects.filter(Q(startDate__lte=Now()) &
 (Q(endDate__isnull=True) | Q(endDate__gt=Now()))).distinct('permit__uuid')

 activeSuspensionPermits =
 
Permit.objects.filter(~Q(uuid__in=Subquery(activeSuspensionPermitUuidsQuery.values('permit__uuid')))
 }}}
 The SQL generated by this is (for PostgreSQL 9.4):
 {{{#!sql
 SELECT "permits"."uuid" FROM "permits" WHERE (NOT ("permits"."uuid" IN
 (CAST(SELECT DISTINCT ON (U0."permits_uuid") U0."permits_uuid" FROM
 "suspensions" U0 INNER JOIN "permits" U1 ON (U0."permits_uuid" =
 U1."uuid") WHERE (U0."start_date" <= (STATEMENT_TIMESTAMP()) AND
 (U0."end_date" IS NULL OR U0."end_date" > (STATEMENT_TIMESTAMP()))) AS
 uuid))));
 }}}

 This generates the following error:
 {{{
 ERROR:  syntax error at or near "SELECT"
 LINE 1: ... FROM "permits" WHERE (NOT ("permits"."uuid" IN (CAST(SELECT
 DIS...
 }}}

 If I edit and run the SQL myself, like this:
 {{{#!sql
 SELECT "permits"."uuid" FROM "permits" WHERE (NOT ("permits"."uuid" IN
 (SELECT DISTINCT ON (U0."permits_uuid") U0."permits_uuid" FROM
 "suspensions" U0 INNER JOIN "permits" U1 ON (U0."permits_uuid" =
 U1."uuid") WHERE (U0."start_date" <= (STATEMENT_TIMESTAMP()) AND
 (U0."end_date" IS NULL OR U0."end_date" > (STATEMENT_TIMESTAMP()))))));
 }}}

 It works fine. So the problem is the `CAST(... as uuid)` that's being
 added.

 As per the mailing list discussion, it turns out that this works:
 {{{#!python
 activeSuspensionPermitUuidsQuery =
 Suspension.objects.filter(Q(startDate__lte=Now()) &
 (Q(endDate__isnull=True) | Q(endDate__gt=Now()))).distinct('permit__uuid')

 activeSuspensionPermits =
 
Permit.objects.filter(~Q(uuid__in=activeSuspensionPermitUuidsQuery.values('permit__uuid'))
 }}}

 However, it should probably work in both cases, unless I misunderstand the
 point/purpose of the `Subquery` object.

--
Ticket URL: <https://code.djangoproject.com/ticket/28199>
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 django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/052.ade357b365bfa3ca10fe8be10c745e14%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to