1
0
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:
Malcolm Tredinnick 2007-12-22 11:16:04 +00:00
parent f47cfe12ae
commit e247b36447
4 changed files with 47 additions and 26 deletions

View File

@ -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).

View File

@ -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)

View File

@ -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

View File

@ -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