Commit 7d03ca9e authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #10161 -- Modified evaluation of query expressions to allow for...

Fixed #10161 -- Modified evaluation of query expressions to allow for operators that take the form of functions. This is mostly for the benefit of Oracle, but it should prove useful later on. Thanks to Ian for the report and feedback on the fix.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9898 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 28605a9c
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -408,6 +408,15 @@ class BaseDatabaseOperations(object):
        """
        pass

    def combine_expression(self, connector, sub_expressions):
        """Combine a list of subexpressions into a single expression, using
        the provided connecting operator. This is required because operators
        can vary between backends (e.g., Oracle with %% and &) and between
        subexpression types (e.g., date expressions)
        """
        conn = ' %s ' % connector
        return conn.join(sub_expressions)

class BaseDatabaseIntrospection(object):
    """
    This class encapsulates all backend-specific introspection utilities
+10 −0
Original line number Diff line number Diff line
@@ -221,6 +221,16 @@ WHEN (new.%(col_name)s IS NULL)
        second = '%s-12-31'
        return [first % value, second % value]

    def combine_expression(self, connector, sub_expressions):
        "Oracle requires special cases for %% and & operators in query expressions"
        if connector == '%%':
            return 'MOD(%s)' % ','.join(sub_expressions)
        elif connector == '&':
            return 'BITAND(%s)' % ','.join(sub_expressions)
        elif connector == '|':
            raise NotImplementedError("Bit-wise or is not supported in Oracle.")
        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)


class DatabaseWrapper(BaseDatabaseWrapper):

+1 −2
Original line number Diff line number Diff line
@@ -74,9 +74,8 @@ class SQLEvaluator(object):
            if sql:
                expressions.append(format % sql)
                expression_params.extend(params)
        conn = ' %s ' % node.connector

        return conn.join(expressions), expression_params
        return connection.ops.combine_expression(node.connector, expressions), expression_params

    def evaluate_leaf(self, node, qn):
        if not qn:
+15 −3
Original line number Diff line number Diff line
@@ -37,14 +37,26 @@ __test__ = {'API_TESTS': """
>>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
...     ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()

>>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')

# We can filter for companies where the number of employees is greater than the
# number of chairs.
>>> company_query.filter(num_employees__gt=F('num_chairs'))
[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}]

>>> Company.objects.filter(num_employees__gt=F('num_chairs'))
[<Company: Example Inc.>, <Company: Test GmbH>]
# We can set one field to have the value of another field
# Make sure we have enough chairs
>>> _ = company_query.update(num_chairs=F('num_employees'))
>>> company_query
[{'num_chairs': 2300, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 3, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 32, 'name': u'Test GmbH', 'num_employees': 32}]

# The relation of a foreign key can become copied over to an other foreign key.
# We can perform arithmetic operations in expressions
# Make sure we have 2 spare chairs
>>> _ =company_query.update(num_chairs=F('num_employees')+2)
>>> company_query
[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]

# The relation of a foreign key can become copied over to an other foreign key.
>>> Company.objects.update(point_of_contact=F('ceo'))
3

+12 −8
Original line number Diff line number Diff line
"""
Spanning tests for all the operations that F() expressions can perform.
"""

from django.conf import settings
from django.db import models

#
@@ -87,11 +87,6 @@ Complex expressions of different connection types are possible.
>>> Number.objects.get(pk=n.pk) # LH Bitwise ands on integers
<Number: 40, 15.500>

>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
>>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
<Number: 58, 15.500>

# Right hand operators

>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
@@ -123,11 +118,20 @@ Complex expressions of different connection types are possible.
>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer'))
>>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers
<Number: 10, 15.500>
"""}

# Oracle doesn't support the Bitwise OR operator.
if settings.DATABASE_ENGINE != 'oracle':
    __test__['API_TESTS'] += """

>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
>>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
<Number: 58, 15.500>

>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer'))
>>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers
<Number: 47, 15.500>


"""}
"""