diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-09-02 02:16:41 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-09-02 02:16:41 +0000 |
| commit | 12f625990392d49018d0397a7c7bf55286f9b212 (patch) | |
| tree | e2f8477ce14a7e82962d4759265f25fb43a826ec /django/db | |
| parent | 3cdfb47e9315672cdf06443b32feb62e1f0c0d42 (diff) | |
Fixed #8439 -- Complex combinations of Q-objects (using both conjunctions and
disjunctions) were producing incorrect SQL when nullable relations were
involved. This fixes that.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@8832 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db')
| -rw-r--r-- | django/db/models/sql/constants.py | 3 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 24 |
2 files changed, 25 insertions, 2 deletions
diff --git a/django/db/models/sql/constants.py b/django/db/models/sql/constants.py index 129a592b31..e14816c965 100644 --- a/django/db/models/sql/constants.py +++ b/django/db/models/sql/constants.py @@ -15,7 +15,8 @@ GET_ITERATOR_CHUNK_SIZE = 100 LOOKUP_SEP = '__' # Constants to make looking up tuple values clearer. -# Join lists +# Join lists (indexes into the tuples that are values in the alias_map +# dictionary in the Query class). TABLE_NAME = 0 RHS_ALIAS = 1 JOIN_TYPE = 2 diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index f15e4ac2f2..73584a65fa 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -716,7 +716,6 @@ class Query(object): alias = table_name self.table_map[alias] = [alias] self.alias_refcount[alias] = 1 - #self.alias_map[alias] = None self.tables.append(alias) return alias, True @@ -1188,6 +1187,8 @@ class Query(object): subtree = False connector = AND for child in q_object.children: + if connector == OR: + refcounts_before = self.alias_refcount.copy() if isinstance(child, Node): self.where.start_subtree(connector) self.add_q(child, used_aliases) @@ -1195,6 +1196,27 @@ class Query(object): else: self.add_filter(child, connector, q_object.negated, can_reuse=used_aliases) + if connector == OR: + # Aliases that were newly added or not used at all need to + # be promoted to outer joins if they are nullable relations. + # (they shouldn't turn the whole conditional into the empty + # set just because they don't match anything). + # FIXME: There's some (a lot of!) overlap with the similar + # OR promotion in add_filter(). It's not quite identical, + # but is very similar. So pulling out the common bits is + # something for later (code smell: too much indentation + # here) + considered = {} + for alias in self.tables: + if alias not in used_aliases: + continue + if (alias not in refcounts_before or + self.alias_refcount[alias] == + refcounts_before[alias]): + parent = self.alias_map[alias][LHS_ALIAS] + must_promote = considered.get(parent, False) + promoted = self.promote_alias(alias, must_promote) + considered[alias] = must_promote or promoted connector = q_object.connector if q_object.negated: self.where.negate() |
