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

Thanks Mariusz Felisiak for review and MySQL advice.
This commit is contained in:
anabelensc 2015-12-14 19:13:21 +00:00 committed by Tim Graham
parent f0ef0c49e9
commit 1c12df4aa6
7 changed files with 37 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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