mirror of https://github.com/django/django.git
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 Ramon <amit.ramon@gmail.com>
|
||||||
Amit Upadhyay <http://www.amitu.com/blog/>
|
Amit Upadhyay <http://www.amitu.com/blog/>
|
||||||
A. Murat Eren <meren@pardus.org.tr>
|
A. Murat Eren <meren@pardus.org.tr>
|
||||||
|
Ana Belen Sarabia <belensarabia@gmail.com>
|
||||||
Ana Krivokapic <https://github.com/infraredgirl>
|
Ana Krivokapic <https://github.com/infraredgirl>
|
||||||
Andi Albrecht <albrecht.andi@gmail.com>
|
Andi Albrecht <albrecht.andi@gmail.com>
|
||||||
André Ericson <de.ericson@gmail.com>
|
André Ericson <de.ericson@gmail.com>
|
||||||
|
|
|
@ -203,8 +203,11 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return 'POW(%s)' % ','.join(sub_expressions)
|
return 'POW(%s)' % ','.join(sub_expressions)
|
||||||
# Convert the result to a signed integer since MySQL's binary operators
|
# Convert the result to a signed integer since MySQL's binary operators
|
||||||
# return an unsigned integer.
|
# return an unsigned integer.
|
||||||
elif connector in ('&', '|'):
|
elif connector in ('&', '|', '<<'):
|
||||||
return 'CONVERT(%s, SIGNED)' % connector.join(sub_expressions)
|
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)
|
return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
|
||||||
|
|
||||||
def get_db_converters(self, expression):
|
def get_db_converters(self, expression):
|
||||||
|
|
|
@ -436,14 +436,17 @@ WHEN (new.%(col_name)s IS NULL)
|
||||||
value.second, value.microsecond)
|
value.second, value.microsecond)
|
||||||
|
|
||||||
def combine_expression(self, connector, sub_expressions):
|
def combine_expression(self, connector, sub_expressions):
|
||||||
"Oracle requires special cases for %% and & operators in query expressions"
|
lhs, rhs = sub_expressions
|
||||||
if connector == '%%':
|
if connector == '%%':
|
||||||
return 'MOD(%s)' % ','.join(sub_expressions)
|
return 'MOD(%s)' % ','.join(sub_expressions)
|
||||||
elif connector == '&':
|
elif connector == '&':
|
||||||
return 'BITAND(%s)' % ','.join(sub_expressions)
|
return 'BITAND(%s)' % ','.join(sub_expressions)
|
||||||
elif connector == '|':
|
elif connector == '|':
|
||||||
lhs, rhs = sub_expressions
|
|
||||||
return 'BITAND(-%(lhs)s-1,%(rhs)s)+%(lhs)s' % {'lhs': lhs, 'rhs': rhs}
|
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 == '^':
|
elif connector == '^':
|
||||||
return 'POWER(%s)' % ','.join(sub_expressions)
|
return 'POWER(%s)' % ','.join(sub_expressions)
|
||||||
return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
|
return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
|
||||||
|
|
|
@ -30,6 +30,8 @@ class Combinable(object):
|
||||||
# usage.
|
# usage.
|
||||||
BITAND = '&'
|
BITAND = '&'
|
||||||
BITOR = '|'
|
BITOR = '|'
|
||||||
|
BITLEFTSHIFT = '<<'
|
||||||
|
BITRIGHTSHIFT = '>>'
|
||||||
|
|
||||||
def _combine(self, other, connector, reversed, node=None):
|
def _combine(self, other, connector, reversed, node=None):
|
||||||
if not hasattr(other, 'resolve_expression'):
|
if not hasattr(other, 'resolve_expression'):
|
||||||
|
@ -76,6 +78,12 @@ class Combinable(object):
|
||||||
def bitand(self, other):
|
def bitand(self, other):
|
||||||
return self._combine(other, self.BITAND, False)
|
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):
|
def __or__(self, other):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Use .bitand() and .bitor() for bitwise logical operations."
|
"Use .bitand() and .bitor() for bitwise logical operations."
|
||||||
|
|
|
@ -367,6 +367,9 @@ Models
|
||||||
:meth:`~django.db.models.Expression.desc` to control
|
:meth:`~django.db.models.Expression.desc` to control
|
||||||
the ordering of null values.
|
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
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -652,11 +652,15 @@ that were modified more than 3 days after they were published::
|
||||||
>>> from datetime import timedelta
|
>>> from datetime import timedelta
|
||||||
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
|
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
|
||||||
|
|
||||||
The ``F()`` objects support bitwise operations by ``.bitand()`` and
|
The ``F()`` objects support bitwise operations by ``.bitand()``, ``.bitor()``,
|
||||||
``.bitor()``, for example::
|
``.bitrightshift()``, and ``.bitleftshift()``. For example::
|
||||||
|
|
||||||
>>> F('somefield').bitand(16)
|
>>> F('somefield').bitand(16)
|
||||||
|
|
||||||
|
.. versionchanged:: 1.11
|
||||||
|
|
||||||
|
Support for ``.bitrightshift()`` and ``.bitleftshift()`` was added.
|
||||||
|
|
||||||
The ``pk`` lookup shortcut
|
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.n1.pk).integer, -64)
|
||||||
self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
|
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):
|
def test_lefthand_bitwise_or(self):
|
||||||
# LH Bitwise or on integers
|
# LH Bitwise or on integers
|
||||||
Number.objects.update(integer=F('integer').bitor(48))
|
Number.objects.update(integer=F('integer').bitor(48))
|
||||||
|
|
Loading…
Reference in New Issue