Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
  • Loading branch information...
commit 7d03ca9e86e366c2c369101621d011eb6ee8b2c2 1 parent 28605a9
Russell Keith-Magee authored February 24, 2009
9  django/db/backends/__init__.py
@@ -408,6 +408,15 @@ def check_aggregate_support(self, aggregate_func):
408 408
         """
409 409
         pass
410 410
 
  411
+    def combine_expression(self, connector, sub_expressions):
  412
+        """Combine a list of subexpressions into a single expression, using
  413
+        the provided connecting operator. This is required because operators
  414
+        can vary between backends (e.g., Oracle with %% and &) and between
  415
+        subexpression types (e.g., date expressions)
  416
+        """
  417
+        conn = ' %s ' % connector
  418
+        return conn.join(sub_expressions)
  419
+
411 420
 class BaseDatabaseIntrospection(object):
412 421
     """
413 422
     This class encapsulates all backend-specific introspection utilities
10  django/db/backends/oracle/base.py
@@ -221,6 +221,16 @@ def year_lookup_bounds_for_date_field(self, value):
221 221
         second = '%s-12-31'
222 222
         return [first % value, second % value]
223 223
 
  224
+    def combine_expression(self, connector, sub_expressions):
  225
+        "Oracle requires special cases for %% and & operators in query expressions"
  226
+        if connector == '%%':
  227
+            return 'MOD(%s)' % ','.join(sub_expressions)
  228
+        elif connector == '&':
  229
+            return 'BITAND(%s)' % ','.join(sub_expressions)
  230
+        elif connector == '|':
  231
+            raise NotImplementedError("Bit-wise or is not supported in Oracle.")
  232
+        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
  233
+
224 234
 
225 235
 class DatabaseWrapper(BaseDatabaseWrapper):
226 236
 
3  django/db/models/sql/expressions.py
@@ -74,9 +74,8 @@ def evaluate_node(self, node, qn):
74 74
             if sql:
75 75
                 expressions.append(format % sql)
76 76
                 expression_params.extend(params)
77  
-        conn = ' %s ' % node.connector
78 77
 
79  
-        return conn.join(expressions), expression_params
  78
+        return connection.ops.combine_expression(node.connector, expressions), expression_params
80 79
 
81 80
     def evaluate_leaf(self, node, qn):
82 81
         if not qn:
18  tests/modeltests/expressions/models.py
@@ -37,14 +37,26 @@ def __unicode__(self):
37 37
 >>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
38 38
 ...     ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()
39 39
 
  40
+>>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')
  41
+
40 42
 # We can filter for companies where the number of employees is greater than the
41 43
 # number of chairs.
  44
+>>> company_query.filter(num_employees__gt=F('num_chairs'))
  45
+[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}]
42 46
 
43  
->>> Company.objects.filter(num_employees__gt=F('num_chairs'))
44  
-[<Company: Example Inc.>, <Company: Test GmbH>]
  47
+# We can set one field to have the value of another field
  48
+# Make sure we have enough chairs
  49
+>>> _ = company_query.update(num_chairs=F('num_employees'))
  50
+>>> company_query
  51
+[{'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}]
45 52
 
46  
-# The relation of a foreign key can become copied over to an other foreign key.
  53
+# We can perform arithmetic operations in expressions
  54
+# Make sure we have 2 spare chairs
  55
+>>> _ =company_query.update(num_chairs=F('num_employees')+2)
  56
+>>> company_query
  57
+[{'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}]
47 58
 
  59
+# The relation of a foreign key can become copied over to an other foreign key.
48 60
 >>> Company.objects.update(point_of_contact=F('ceo'))
49 61
 3
50 62
 
20  tests/regressiontests/expressions_regress/models.py
... ...
@@ -1,7 +1,7 @@
1 1
 """
2 2
 Spanning tests for all the operations that F() expressions can perform.
3 3
 """
4  
-
  4
+from django.conf import settings
5 5
 from django.db import models
6 6
 
7 7
 #
@@ -87,11 +87,6 @@ def __unicode__(self):
87 87
 >>> Number.objects.get(pk=n.pk) # LH Bitwise ands on integers
88 88
 <Number: 40, 15.500>
89 89
 
90  
->>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
91  
->>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
92  
->>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
93  
-<Number: 58, 15.500>
94  
-
95 90
 # Right hand operators
96 91
 
97 92
 >>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
@@ -123,11 +118,20 @@ def __unicode__(self):
123 118
 >>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer'))
124 119
 >>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers
125 120
 <Number: 10, 15.500>
  121
+"""}
  122
+
  123
+# Oracle doesn't support the Bitwise OR operator.
  124
+if settings.DATABASE_ENGINE != 'oracle':
  125
+    __test__['API_TESTS'] += """
  126
+
  127
+>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
  128
+>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48)
  129
+>>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers
  130
+<Number: 58, 15.500>
126 131
 
127 132
 >>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
128 133
 >>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer'))
129 134
 >>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers
130 135
 <Number: 47, 15.500>
131 136
 
132  
-
133  
-"""}
  137
+"""

0 notes on commit 7d03ca9

Please sign in to comment.
Something went wrong with that request. Please try again.