#10977: In some cases, the intersection of two QuerySets is empty when it
shouldn't
be
---------------------------------------------------+------------------------
Reporter: zain | Owner: clement
Status: new | Milestone:
Component: Database layer (models, ORM) | Version: 1.0
Resolution: | Keywords:
Stage: Unreviewed | Has_patch: 0
Needs_docs: 0 | Needs_tests: 0
Needs_better_patch: 0 |
---------------------------------------------------+------------------------
Comment (by clement):
Well, it seems there's a bug in BaseQuery.combine, when relabeling
aliases. It happens when the rhs query object contains JOIN ''from'' an
aliased table; they are converted to JOINs ''from'' the original table
because the code is building a new join using data coming from
'''.rev_join_map''' (and not '''.alias_map''') :
{{{
#!python
# django/db/models/sql/query.py, line 501
for alias in rhs.tables:
if not rhs.alias_refcount[alias]:
# An unused alias.
continue
promote = (rhs.alias_map[alias][JOIN_TYPE] == self.LOUTER)
new_alias = self.join(rhs.rev_join_map[alias],
(conjunction and not first), used, promote, not
conjunction)
used.add(new_alias)
change_map[alias] = new_alias
}}}
To recreate this bug, any query joining from an alias will be enough, and
will give an incorrect SQL query string when combined (right-operand) to a
simple query, on the same base object. For example, with :
{{{
#!python
class ModelA(models.Model):
text = models.CharField(max_length=100)
a = models.ForeignKey('ModelA')
b = models.ForeignKey('ModelB')
class ModelB(models.Model):
text = models.CharField(max_length=100)
}}}
That will give:
{{{
#!python
>>> qs_simple = ModelA.objects.all()
>>> qs_joined =
ModelA.objects.filter(b__text__icontains='foo').filter(a__b__text__icontains='bar')
>>> str(qs_simple.query) # Formatted for clarity
SELECT "queries_modela"."id", "queries_modela"."text",
"queries_modela"."a_id", "queries_modela"."b_id"
FROM "queries_modela"
>>> str(qs_joined.query) # Formatted for clarity
SELECT "queries_modela"."id", "queries_modela"."text",
"queries_modela"."a_id", "queries_modela"."b_id"
FROM "queries_modela"
INNER JOIN "queries_modelb"
ON ("queries_modela"."b_id" = "queries_modelb"."id")
INNER JOIN "queries_modela" T3
ON ("queries_modela"."a_id" = T3."id")
INNER JOIN "queries_modelb" T4
ON (T3."b_id" = T4."id")
WHERE ("queries_modelb"."text" LIKE %foo% ESCAPE '\' AND T4."text" LIKE
%bar% ESCAPE '\' )
>>> str((qs_simple & qs_joined).query) # Formatted for clarity
SELECT "queries_modela"."id", "queries_modela"."text",
"queries_modela"."a_id", "queries_modela"."b_id"
FROM "queries_modela"
INNER JOIN "queries_modelb"
ON ("queries_modela"."b_id" = "queries_modelb"."id")
INNER JOIN "queries_modela" T3
ON ("queries_modela"."a_id" = T3."id")
INNER JOIN "queries_modelb" T4
ON ("queries_modela"."b_id" = T4."id")
WHERE ("queries_modelb"."text" LIKE %foo% ESCAPE '\' AND T4."text" LIKE
%bar% ESCAPE '\' )
}}}
Seeing here that the '''T4''' join ON condition becomes incorrect when
'''qs_joined''' is combined to '''qs_simple'''
--
Ticket URL: <http://code.djangoproject.com/ticket/10977#comment:2>
Django <http://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 post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---