Repository: kayak/pypika
Pull request #732 (2023-06-14 09:43:49)

fix(terms): fixed EmptyCriterion bug with ComplexCriterion
issue: https://github.com/kayak/pypika/issues/327

AzisK: For some reason the CI tests did not run with this PR. Could you update the code with the newest from the master branch?
GeorgiySurkov: Yes, of course!
coveralls: 
[![Coverage Status](https://coveralls.io/builds/62835024/badge)](https://coveralls.io/builds/62835024)

coverage: 98.43% (+0.007%) from 98.423% when pulling **ab62819ac76f94dd76a1caf4d8e6e1fcb91fb8d4 on GeorgiySurkov:master** into **ea9cc5b222dd9f056d072ceebce25f5980d4c2dd on kayak:master**.

AzisK: It looks good and I am merging this but version bump will come later

Patch:
diff --git a/pypika/terms.py b/pypika/terms.py
index c522550..2139ea6 100644
--- a/pypika/terms.py
+++ b/pypika/terms.py
@@ -486,12 +486,18 @@ class SystemTimeValue(LiteralValue):
 
 class Criterion(Term):
     def __and__(self, other: Any) -> "ComplexCriterion":
+        if isinstance(other, EmptyCriterion):
+            return self
         return ComplexCriterion(Boolean.and_, self, other)
 
     def __or__(self, other: Any) -> "ComplexCriterion":
+        if isinstance(other, EmptyCriterion):
+            return self
         return ComplexCriterion(Boolean.or_, self, other)
 
     def __xor__(self, other: Any) -> "ComplexCriterion":
+        if isinstance(other, EmptyCriterion):
+            return self
         return ComplexCriterion(Boolean.xor_, self, other)
 
     @staticmethod
@@ -532,6 +538,9 @@ class EmptyCriterion(Criterion):
     def __xor__(self, other: Any) -> Any:
         return other
 
+    def __invert__(self) -> Any:
+        return self
+
 
 class Field(Criterion, JSON):
     def __init__(


Test patch:
diff --git a/pypika/tests/test_criterions.py b/pypika/tests/test_criterions.py
index 2ecaa13..37b94e8 100644
--- a/pypika/tests/test_criterions.py
+++ b/pypika/tests/test_criterions.py
@@ -791,6 +791,62 @@ class EmptyCriterionTests(unittest.TestCase):
 
         self.assertEqual(len(empty_criterion.fields_()), 0)
 
+    def test_empty_criterion_on_the_left(self):
+        t = Table("test1")
+        q = EmptyCriterion() & t.f1 == "v1"
+
+        self.assertEqual(
+            '"test1"."f1"=\'v1\'',
+            q.get_sql(with_namespace=True),
+        )
+
+    def test_empty_criterion_on_the_right(self):
+        t = Table("test1")
+        q = (t.f1 == "v1") & EmptyCriterion()
+
+        self.assertEqual(
+            '"test1"."f1"=\'v1\'',
+            q.get_sql(with_namespace=True),
+        )
+
+    def test_invertion_of_the_empty_criterion(self):
+        t = Table("test1")
+        q = (t.f1 == "v1") & ~EmptyCriterion()
+
+        self.assertEqual(
+            '"test1"."f1"=\'v1\'',
+            q.get_sql(with_namespace=True),
+        )
+
+    def test_different_operations_with_empty_criterion(self):
+        t = Table("test1")
+        q1 = (t.f1 == "v1") & EmptyCriterion()
+        q2 = (t.f1 == "v1") | EmptyCriterion()
+        q3 = (t.f1 == "v1") ^ EmptyCriterion()
+
+        expected_sql = '"test1"."f1"=\'v1\''
+        self.assertEqual(
+            expected_sql,
+            q1.get_sql(with_namespace=True),
+        )
+        self.assertEqual(
+            expected_sql,
+            q2.get_sql(with_namespace=True),
+        )
+        self.assertEqual(
+            expected_sql,
+            q3.get_sql(with_namespace=True),
+        )
+
+    def test_more_than_one_empty_criterions(self):
+        t = Table("test1")
+        q = EmptyCriterion() & ~(EmptyCriterion() | EmptyCriterion()) & (t.f1 == "v1") & ~EmptyCriterion()
+
+        self.assertEqual(
+            '"test1"."f1"=\'v1\'',
+            q.get_sql(with_namespace=True),
+        )
+
 
 class AllTests(unittest.TestCase):
     def test_zero_args_returns_empty_criterion(self):