mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
queryset-refactor: Work around the fact that "where id is NULL" can return different results in different circumstances in MySQL(!!).
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6967 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
f47cfe12ae
commit
e247b36447
@ -6,6 +6,9 @@ the SQL domain.
|
||||
class EmptyResultSet(Exception):
|
||||
pass
|
||||
|
||||
class FullResultSet(Exception):
|
||||
pass
|
||||
|
||||
class Aggregate(object):
|
||||
"""
|
||||
Base class for all aggregate-related classes (min, max, avg, count, sum).
|
||||
|
@ -15,7 +15,7 @@ from django.utils.tree import Node
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.dispatch import dispatcher
|
||||
from django.db.models import signals
|
||||
from django.db.models.sql.where import WhereNode, AND, OR
|
||||
from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR
|
||||
from django.db.models.sql.datastructures import Count, Date
|
||||
from django.db.models.fields import FieldDoesNotExist, Field, related
|
||||
from django.contrib.contenttypes import generic
|
||||
@ -311,19 +311,18 @@ class Query(object):
|
||||
w.relabel_aliases(change_map)
|
||||
if not self.where:
|
||||
# Since 'self' matches everything, add an explicit "include
|
||||
# everything" (pk is not NULL) where-constraint so that
|
||||
# connections between the where clauses won't exclude valid
|
||||
# results.
|
||||
# everything" where-constraint so that connections between the
|
||||
# where clauses won't exclude valid results.
|
||||
alias = self.join((None, self.model._meta.db_table, None, None))
|
||||
pk = self.model._meta.pk
|
||||
self.where.add([alias, pk.column, pk, 'isnull', False], AND)
|
||||
self.where.add(EverythingNode(), AND)
|
||||
elif self.where:
|
||||
# rhs has an empty where clause. Make it match everything (see
|
||||
# above for reasoning).
|
||||
w = WhereNode()
|
||||
alias = self.join((None, self.model._meta.db_table, None, None))
|
||||
pk = self.model._meta.pk
|
||||
w.add([alias, pk.column, pk, 'isnull', False], AND)
|
||||
w.add(EverythingNode(), AND)
|
||||
else:
|
||||
w = WhereNode()
|
||||
self.where.add(w, connector)
|
||||
|
@ -5,7 +5,7 @@ import datetime
|
||||
|
||||
from django.utils import tree
|
||||
from django.db import connection
|
||||
from datastructures import EmptyResultSet
|
||||
from datastructures import EmptyResultSet, FullResultSet
|
||||
|
||||
# Connection types
|
||||
AND = 'AND'
|
||||
@ -43,26 +43,36 @@ class WhereNode(tree.Node):
|
||||
result_params = []
|
||||
empty = True
|
||||
for child in node.children:
|
||||
if hasattr(child, 'as_sql'):
|
||||
sql, params = child.as_sql(qn=qn)
|
||||
format = '(%s)'
|
||||
elif isinstance(child, tree.Node):
|
||||
sql, params = self.as_sql(child, qn)
|
||||
if child.negated:
|
||||
format = 'NOT (%s)'
|
||||
else:
|
||||
try:
|
||||
if hasattr(child, 'as_sql'):
|
||||
sql, params = child.as_sql(qn=qn)
|
||||
format = '(%s)'
|
||||
else:
|
||||
try:
|
||||
elif isinstance(child, tree.Node):
|
||||
sql, params = self.as_sql(child, qn)
|
||||
if child.negated:
|
||||
format = 'NOT (%s)'
|
||||
else:
|
||||
format = '(%s)'
|
||||
else:
|
||||
sql, params = self.make_atom(child, qn)
|
||||
format = '%s'
|
||||
except EmptyResultSet:
|
||||
if self.connector == AND and not node.negated:
|
||||
# We can bail out early in this particular case (only).
|
||||
raise
|
||||
elif node.negated:
|
||||
empty = False
|
||||
continue
|
||||
except EmptyResultSet:
|
||||
if self.connector == AND and not node.negated:
|
||||
# We can bail out early in this particular case (only).
|
||||
raise
|
||||
elif node.negated:
|
||||
empty = False
|
||||
continue
|
||||
except FullResultSet:
|
||||
if self.connector == OR:
|
||||
if node.negated:
|
||||
empty = True
|
||||
break
|
||||
# We match everything. No need for any constraints.
|
||||
return '', []
|
||||
if node.negated:
|
||||
empty = True
|
||||
continue
|
||||
empty = False
|
||||
if sql:
|
||||
result.append(format % sql)
|
||||
@ -156,3 +166,12 @@ class WhereNode(tree.Node):
|
||||
val = child[0]
|
||||
child[0] = change_map.get(val, val)
|
||||
|
||||
class EverythingNode(object):
|
||||
"""
|
||||
A node that matches everything.
|
||||
"""
|
||||
def as_sql(self, qn=None):
|
||||
raise FullResultSet
|
||||
|
||||
def relabel_aliases(self, change_map, node=None):
|
||||
return
|
||||
|
@ -26,11 +26,11 @@ __test__ = {'API_TESTS':"""
|
||||
|
||||
# Exact query with value None returns nothing ("is NULL" in sql, but every 'id'
|
||||
# field has a value).
|
||||
>>> Choice.objects.filter(id__exact=None)
|
||||
>>> Choice.objects.filter(choice__exact=None)
|
||||
[]
|
||||
|
||||
Excluding the previous result returns everything.
|
||||
>>> Choice.objects.exclude(id=None).order_by('id')
|
||||
>>> Choice.objects.exclude(choice=None).order_by('id')
|
||||
[<Choice: Choice: Because. in poll Q: Why? >, <Choice: Choice: Why Not? in poll Q: Why? >]
|
||||
|
||||
# Valid query, but fails because foo isn't a keyword
|
||||
|
Loading…
x
Reference in New Issue
Block a user