Browse Source

Fixed #25912 -- Added binary left/right shift operators to F expressions.

Thanks Mariusz Felisiak for review and MySQL advice.
anabelensc 9 years ago
parent
commit
1c12df4aa6

+ 1 - 0
AUTHORS

@@ -42,6 +42,7 @@ answer newbie questions, and generally made Django that much better:
     Amit Ramon <amit.ramon@gmail.com>
     Amit Upadhyay <http://www.amitu.com/blog/>
     A. Murat Eren <meren@pardus.org.tr>
+    Ana Belen Sarabia <belensarabia@gmail.com>
     Ana Krivokapic <https://github.com/infraredgirl>
     Andi Albrecht <albrecht.andi@gmail.com>
     André Ericson <de.ericson@gmail.com>

+ 4 - 1
django/db/backends/mysql/operations.py

@@ -203,8 +203,11 @@ class DatabaseOperations(BaseDatabaseOperations):
             return 'POW(%s)' % ','.join(sub_expressions)
         # Convert the result to a signed integer since MySQL's binary operators
         # return an unsigned integer.
-        elif connector in ('&', '|'):
+        elif connector in ('&', '|', '<<'):
             return 'CONVERT(%s, SIGNED)' % connector.join(sub_expressions)
+        elif connector == '>>':
+            lhs, rhs = sub_expressions
+            return 'FLOOR(%(lhs)s / POW(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
         return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
 
     def get_db_converters(self, expression):

+ 5 - 2
django/db/backends/oracle/operations.py

@@ -436,14 +436,17 @@ WHEN (new.%(col_name)s IS NULL)
                                value.second, value.microsecond)
 
     def combine_expression(self, connector, sub_expressions):
-        "Oracle requires special cases for %% and & operators in query expressions"
+        lhs, rhs = sub_expressions
         if connector == '%%':
             return 'MOD(%s)' % ','.join(sub_expressions)
         elif connector == '&':
             return 'BITAND(%s)' % ','.join(sub_expressions)
         elif connector == '|':
-            lhs, rhs = sub_expressions
             return 'BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s' % {'lhs': lhs, 'rhs': rhs}
+        elif connector == '<<':
+            return '(%(lhs)s * POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
+        elif connector == '>>':
+            return 'FLOOR(%(lhs)s / POWER(2, %(rhs)s))' % {'lhs': lhs, 'rhs': rhs}
         elif connector == '^':
             return 'POWER(%s)' % ','.join(sub_expressions)
         return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)

+ 8 - 0
django/db/models/expressions.py

@@ -30,6 +30,8 @@ class Combinable(object):
     # usage.
     BITAND = '&'
     BITOR = '|'
+    BITLEFTSHIFT = '<<'
+    BITRIGHTSHIFT = '>>'
 
     def _combine(self, other, connector, reversed, node=None):
         if not hasattr(other, 'resolve_expression'):
@@ -76,6 +78,12 @@ class Combinable(object):
     def bitand(self, other):
         return self._combine(other, self.BITAND, False)
 
+    def bitleftshift(self, other):
+        return self._combine(other, self.BITLEFTSHIFT, False)
+
+    def bitrightshift(self, other):
+        return self._combine(other, self.BITRIGHTSHIFT, False)
+
     def __or__(self, other):
         raise NotImplementedError(
             "Use .bitand() and .bitor() for bitwise logical operations."

+ 3 - 0
docs/releases/1.11.txt

@@ -367,6 +367,9 @@ Models
   :meth:`~django.db.models.Expression.desc` to control
   the ordering of null values.
 
+* The new ``F`` expression ``bitleftshift()`` and ``bitrightshift()`` methods
+  allow :ref:`bitwise shift operations <using-f-expressions-in-filters>`.
+
 Requests and Responses
 ~~~~~~~~~~~~~~~~~~~~~~
 

+ 6 - 2
docs/topics/db/queries.txt

@@ -652,11 +652,15 @@ that were modified more than 3 days after they were published::
     >>> from datetime import timedelta
     >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
 
-The ``F()`` objects support bitwise operations by ``.bitand()`` and
-``.bitor()``, for example::
+The ``F()`` objects support bitwise operations by ``.bitand()``, ``.bitor()``,
+``.bitrightshift()``, and ``.bitleftshift()``. For example::
 
     >>> F('somefield').bitand(16)
 
+.. versionchanged:: 1.11
+
+    Support for ``.bitrightshift()`` and ``.bitleftshift()`` was added.
+
 The ``pk`` lookup shortcut
 --------------------------
 

+ 10 - 0
tests/expressions/tests.py

@@ -745,6 +745,16 @@ class ExpressionOperatorTests(TestCase):
         self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -64)
         self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
 
+    def test_lefthand_bitwise_left_shift_operator(self):
+        Number.objects.update(integer=F('integer').bitleftshift(2))
+        self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 168)
+        self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -168)
+
+    def test_lefthand_bitwise_right_shift_operator(self):
+        Number.objects.update(integer=F('integer').bitrightshift(2))
+        self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 10)
+        self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -11)
+
     def test_lefthand_bitwise_or(self):
         # LH Bitwise or on integers
         Number.objects.update(integer=F('integer').bitor(48))