Skip to content

Commit

Permalink
Detect usage of a subquery as a constraint value and fail with a Data…
Browse files Browse the repository at this point in the history
…baseError (other exception may get swallowed and result in an empty set being returned). See issue #8.
  • Loading branch information
wrwrwr committed Feb 25, 2012
1 parent 9e82134 commit d81c911
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 5 deletions.
22 changes: 17 additions & 5 deletions djangotoolbox/db/basecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.conf import settings
from django.db.models.fields import NOT_PROVIDED, DecimalField
from django.db.models.query import QuerySet
from django.db.models.sql import aggregates as sqlaggregates
from django.db.models.sql.compiler import SQLCompiler
from django.db.models.sql.constants import LOOKUP_SEP, MULTI, SINGLE
Expand Down Expand Up @@ -211,18 +212,29 @@ def _normalize_lookup_value(self, lookup_type, value, field, annotation):
def _get_children(self, children):
"""
Filters out nodes of the given contraint tree not needed for
nonrel queries.
nonrel queries; checks that given constraints are supported.
"""
result = []
for child in children:

# Remove leafs that were automatically added by
# sql.Query.add_filter to handle negations of outer joins.
if isinstance(child, tuple):
constraint = child[0]
lookup_type = child[1]
constraint, lookup_type, _, value = child

# When doing a lookup using a QuerySet Django would use
# a subquery, but this won't work for nonrel.
# TODO: Add a supports_subqueries feature and let
# Django evaluate subqueries instead of passing
# them as SQL strings (QueryWrappers) to
# filtering.
if isinstance(value, QuerySet):
raise DatabaseError("Subqueries are not supported (yet).")

# Remove leafs that were automatically added by
# sql.Query.add_filter to handle negations of outer
# joins.
if lookup_type == 'isnull' and constraint.field is None:
continue

result.append(child)
return result

Expand Down
27 changes: 27 additions & 0 deletions djangotoolbox/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import with_statement

from django.core import serializers
from django.db import models
from django.db.models import Q
Expand Down Expand Up @@ -633,3 +635,28 @@ def test_marked_strings(self):
self.assertEqual(
list(String.objects.filter(s__startswith=mark_for_escaping('c'))),
[c])


class FeaturesTest(TestCase):
"""
Some things are unlikely to cause problems for SQL back-ends, but
require special handling in nonrel.
"""

def test_subqueries(self):
"""
Django includes SQL statements as WHERE tree values when
filtering using a QuerySet -- this won't "just work" with
nonrel back-ends.
TODO: Subqueries handling may require a bit of Django
changing, but should be easy to support.
"""
target = Target.objects.create(index=1)
source = Source.objects.create(index=2, target=target)
targets = Target.objects.all()
with self.assertRaises(DatabaseError):
sources = Source.objects.get(target__in=targets)
self.assertEqual(
Source.objects.get(target__in=list(targets)),
source)

0 comments on commit d81c911

Please sign in to comment.