Repository: kayak/pypika
Pull request #288 (2019-08-07 11:58:38)

Support for Bitwise AND
Support for Bitwise AND for Field.

Usage:
```
table = pypika.Table("t")
table.foo.bitwiseand(10) # Returns ("foo" & 10)
table.foo.bitwiseand(10) == 10 # Returns ("foo" & 10)=10
```

While using PyPika in our company I needed to use bitwise and in where clause and I found out that pypika does not support this feature yet. So here it is.

I am happy to change name of functions/class/etc. if you have different opinion about it, but I would love to have this feature in PyPika. :)


Patch:
diff --git a/README.rst b/README.rst
index af8e05a..d5b9d98 100644
--- a/README.rst
+++ b/README.rst
@@ -97,7 +97,7 @@ Using ``pypika.Table`` based select alias
 .. code-block:: python
 
     customers = Table('customers')
-    q = customers.select(customers.id, customers.fname)
+    q = customers.select(custimers.id, customers.fname)
 
 query will return in the following SQL:
 
diff --git a/pypika/functions.py b/pypika/functions.py
index d4e527d..52cf3c4 100644
--- a/pypika/functions.py
+++ b/pypika/functions.py
@@ -135,11 +135,6 @@ class DateDiff(Function):
         super(DateDiff, self).__init__('DATEDIFF', interval, start_date, end_date, alias=alias)
 
 
-class TimeDiff(Function):
-    def __init__(self, start_time, end_time, alias=None):
-        super(TimeDiff, self).__init__('TIMEDIFF', start_time, end_time, alias=alias)
-
-
 class DateAdd(Function):
     def __init__(self, date_part, interval, term, alias=None):
         super(DateAdd, self).__init__('DATE_ADD', date_part, interval, term, alias=alias)
diff --git a/pypika/terms.py b/pypika/terms.py
index e765011..fad1ba8 100644
--- a/pypika/terms.py
+++ b/pypika/terms.py
@@ -102,6 +102,9 @@ class Term:
     def notnull(self):
         return self.isnull().negate()
 
+    def bitwiseand(self, value):
+        return BitwiseAndCriterion(self, value)
+
     def gt(self, other):
         return self > other
 
@@ -752,6 +755,40 @@ class BetweenCriterion(Criterion):
         return self.term.fields() if self.term.fields else []
 
 
+class BitwiseAndCriterion(Criterion):
+    def __init__(self, term, value, alias=None):
+        super(BitwiseAndCriterion, self).__init__(alias)
+        self.term = term
+        self.value = value
+
+    @property
+    def tables_(self):
+        return self.term.tables_
+
+    @builder
+    def replace_table(self, current_table, new_table):
+        """
+        Replaces all occurrences of the specified table with the new table. Useful when reusing fields across queries.
+
+        :param current_table:
+            The table to be replaced.
+        :param new_table:
+            The table to replace with.
+        :return:
+            A copy of the criterion with the tables replaced.
+        """
+        self.term = self.term.replace_table(current_table, new_table)
+
+    def get_sql(self, **kwargs):
+        return "({term} & {value})".format(
+                term=self.term.get_sql(**kwargs),
+                value=self.value,
+        )
+
+    def fields(self):
+        return self.term.fields() if self.term.fields else []
+
+
 class NullCriterion(Criterion):
     def __init__(self, term, alias=None):
         super(NullCriterion, self).__init__(alias)


Test patch:
diff --git a/pypika/tests/test_criterions.py b/pypika/tests/test_criterions.py
index e390725..c8f418b 100644
--- a/pypika/tests/test_criterions.py
+++ b/pypika/tests/test_criterions.py
@@ -245,6 +245,13 @@ class CriterionTests(unittest.TestCase):
         self.assertEqual('"foo">=1', str(c1))
         self.assertEqual('"crit"."foo">=-1', str(c2))
 
+    def test__criterion_bitwise_and(self):
+        c1 = Field("foo").bitwiseand(2)
+        c2 = Field("foo", table=self.t).bitwiseand(10) == 2
+
+        self.assertEqual('("foo" & 2)', str(c1))
+        self.assertEqual('("crit"."foo" & 10)=2', str(c2))
+
 
 class NotTests(unittest.TestCase):
     table_abc, table_efg = Table('abc', alias='cx0'), Table('efg', alias='cx1')
diff --git a/pypika/tests/test_functions.py b/pypika/tests/test_functions.py
index 75f3a09..fcb3add 100644
--- a/pypika/tests/test_functions.py
+++ b/pypika/tests/test_functions.py
@@ -576,10 +576,6 @@ class DateFunctionsTests(unittest.TestCase):
         a = fn.TimestampAdd('year', 1, '2017-10-01')
         self.assertEqual(str(a), "TIMESTAMPADD('year',1,'2017-10-01')")
 
-    def test_time_diff(self):
-        a = fn.TimeDiff('18:00:00', '10:00:00')
-        self.assertEqual(str(a), "TIMEDIFF('18:00:00','10:00:00')")
-
     def test_date_add(self):
         a = fn.DateAdd('year', 1, '2017-10-01')
         self.assertEqual(str(a), "DATE_ADD('year',1,'2017-10-01')")