Fixed #25912 -- Added binary left/right shift operators to F expressions.
Thanks Mariusz Felisiak for review and MySQL advice.
This commit is contained in:
parent
f0ef0c49e9
commit
1c12df4aa6
1
AUTHORS
1
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>
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue