Opened 18 years ago
Last modified 18 years ago
#4289 closed
Misbehaving Q objects in boulder-oracle-sprint branch — at Initial Version
| Reported by: | Ben Khoo | Owned by: | Jacob | 
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev | 
| Severity: | Keywords: | Q Oracle sqlite OR qs-rf-fixed | |
| Cc: | mir@… | Triage Stage: | Accepted | 
| Has patch: | no | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | no | 
| Easy pickings: | no | UI/UX: | no | 
Description
Hi,
The following ticket will try to highlight a problem that I have found when using the 'OR' operator on Q objects in the boulder-oracle-sprint branch.
My model looks like this.
class Test(models.Model):
    num = models.IntegerField()
    def __str__(self):
        return 'num=%d'%self.num
The following code will highlight the bug:
In [1]: from testapp.models import * In [2]: from django.db.models import Q In [3]: for i in [4,8,12]: ...: Test(num=i).save() ...: In [4]: Test.objects.filter(num__lt=4) Out[4]: [] In [5]: Test.objects.filter(num__gt=8, num__lt=12) Out[5]: [] In [6]: Test.objects.filter(Q(num__lt = 4) | Q(num__gt=8, num__lt=12)) Out[6]: [<Test: num=12>, <Test: num=4>, <Test: num=8>] In [7]: Test.objects.filter(Q(num__gt=8, num__lt=12) | Q(num__lt = 4)) Out[7]: [<Test: num=12>, <Test: num=4>, <Test: num=8>] In [8]: Test.objects.filter(Q(num__gt=8) & Q(num__lt=12) | Q(num__lt = 4)) Out[8]: []
Lines 6 and 7 illustrate the bug. 
The query appears to indicate that there are three Test objects where 'num' is less than 4 or, greater than 8 and less than 12.
Lines 4 and 5 show that no such object should exist.
Line 8 shows how I would expect the query to run.
The following is the formated (but otherwise unmodified) SQL query produced by django.
SELECT * 
FROM "TESTAPP_TEST"
WHERE (("TESTAPP_TEST"."NUM" < 4 OR 
        "TESTAPP_TEST"."NUM" > 8 OR 
        "TESTAPP_TEST"."NUM" < 12))
The issue is that the second 'OR' operator should be an AND operator. Also for the sake of safety/sanity I feel that queries specified within the Q objects should also be surrounded by a bracket.
The corrected query should read
SELECT * 
FROM "TESTAPP_TEST"
WHERE ((("TESTAPP_TEST"."NUM" < 4) OR 
        ("TESTAPP_TEST"."NUM" > 8 AND
         "TESTAPP_TEST"."NUM" < 12)))
Regards
Ben