Django

Code

Changeset 8832

Show
Ignore:
Timestamp:
09/01/08 21:16:41 (3 months ago)
Author:
mtredinnick
Message:

Fixed #8439 -- Complex combinations of Q-objects (using both conjunctions and
disjunctions) were producing incorrect SQL when nullable relations were
involved. This fixes that.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/sql/constants.py

    r7490 r8832  
    1616 
    1717# Constants to make looking up tuple values clearer. 
    18 # Join lists 
     18# Join lists (indexes into the tuples that are values in the alias_map 
     19# dictionary in the Query class). 
    1920TABLE_NAME = 0 
    2021RHS_ALIAS = 1 
  • django/trunk/django/db/models/sql/query.py

    r8831 r8832  
    717717            self.table_map[alias] = [alias] 
    718718        self.alias_refcount[alias] = 1 
    719         #self.alias_map[alias] = None 
    720719        self.tables.append(alias) 
    721720        return alias, True 
     
    11891188            connector = AND 
    11901189            for child in q_object.children: 
     1190                if connector == OR: 
     1191                    refcounts_before = self.alias_refcount.copy() 
    11911192                if isinstance(child, Node): 
    11921193                    self.where.start_subtree(connector) 
     
    11961197                    self.add_filter(child, connector, q_object.negated, 
    11971198                            can_reuse=used_aliases) 
     1199                if connector == OR: 
     1200                    # Aliases that were newly added or not used at all need to 
     1201                    # be promoted to outer joins if they are nullable relations. 
     1202                    # (they shouldn't turn the whole conditional into the empty 
     1203                    # set just because they don't match anything). 
     1204                    # FIXME: There's some (a lot of!) overlap with the similar 
     1205                    # OR promotion in add_filter(). It's not quite identical, 
     1206                    # but is very similar. So pulling out the common bits is 
     1207                    # something for later (code smell: too much indentation 
     1208                    # here) 
     1209                    considered = {} 
     1210                    for alias in self.tables: 
     1211                        if alias not in used_aliases: 
     1212                            continue 
     1213                        if (alias not in refcounts_before or 
     1214                                self.alias_refcount[alias] == 
     1215                                refcounts_before[alias]): 
     1216                            parent = self.alias_map[alias][LHS_ALIAS] 
     1217                            must_promote = considered.get(parent, False) 
     1218                            promoted = self.promote_alias(alias, must_promote) 
     1219                            considered[alias] = must_promote or promoted 
    11981220                connector = q_object.connector 
    11991221            if q_object.negated: 
  • django/trunk/tests/regressiontests/queries/models.py

    r8794 r8832  
    224224    name = models.CharField(max_length=20) 
    225225    order = models.IntegerField() 
    226      
     226 
    227227    def __unicode__(self): 
    228228        return self.name 
    229          
     229 
    230230__test__ = {'API_TESTS':""" 
    231231>>> t1 = Tag.objects.create(name='t1') 
     
    238238>>> n2 = Note.objects.create(note='n2', misc='bar') 
    239239>>> n3 = Note.objects.create(note='n3', misc='foo') 
     240 
     241>>> ann1 = Annotation.objects.create(name='a1', tag=t1) 
     242>>> ann1.notes.add(n1) 
     243>>> ann2 = Annotation.objects.create(name='a2', tag=t4) 
     244>>> ann2.notes.add(n2, n3) 
    240245 
    241246Create these out of order so that sorting by 'id' will be different to sorting 
     
    843848 
    844849Bug #7277 
    845 >>> ann1 = Annotation.objects.create(name='a1', tag=t1) 
    846 >>> ann1.notes.add(n1) 
    847850>>> n1.annotation_set.filter(Q(tag=t5) | Q(tag__children=t5) | Q(tag__children__children=t5)) 
    848851[<Annotation: a1>] 
     
    932935>>> ReservedName.objects.all().order_by('order') 
    933936[<ReservedName: b>, <ReservedName: a>] 
    934  
    935937>>> ReservedName.objects.extra(select={'stuff':'name'}, order_by=('order','stuff')) 
    936938[<ReservedName: b>, <ReservedName: a>] 
    937   
     939 
     940Bug #8439 -- complex combinations of conjunctions, disjunctions and nullable 
     941relations. 
     942>>> Author.objects.filter(Q(item__note__extrainfo=e2)|Q(report=r1, name='xyz')) 
     943[<Author: a2>] 
     944>>> Author.objects.filter(Q(report=r1, name='xyz')|Q(item__note__extrainfo=e2)) 
     945[<Author: a2>] 
     946>>> Annotation.objects.filter(Q(tag__parent=t1)|Q(notes__note='n1', name='a1')) 
     947[<Annotation: a1>] 
     948>>> xx = ExtraInfo.objects.create(info='xx', note=n3) 
     949>>> Note.objects.filter(Q(extrainfo__author=a1)|Q(extrainfo=xx)) 
     950[<Note: n1>, <Note: n3>] 
     951>>> xx.delete() 
     952>>> q = Note.objects.filter(Q(extrainfo__author=a1)|Q(extrainfo=xx)).query 
     953>>> len([x[2] for x in q.alias_map.values() if x[2] == q.LOUTER and q.alias_refcount[x[1]]]) 
     954
     955 
    938956"""} 
    939957