]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fully implemented the IS and IS NOT operators with
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Apr 2013 23:12:47 +0000 (19:12 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Apr 2013 23:12:47 +0000 (19:12 -0400)
regards to the True/False constants.  An expression like
``col.is_(True)`` will now render ``col IS true``
on the target platform, rather than converting the True/
False constant to an integer bound parameter.
This allows the ``is_()`` operator to work on MySQL when
given True/False constants.
[ticket:2682]

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/sql/expression.py
test/sql/test_operators.py

index 489cad5824a195c3ce0692928c0f2bac59be5d95..1f89984527849fbeb4d9a4dc8710b2bd7fec73cf 100644 (file)
@@ -6,6 +6,18 @@
 .. changelog::
     :version: 0.8.1
 
+    .. change::
+      :tags: bug, sql, mysql
+      :tickets: 2682
+
+      Fully implemented the IS and IS NOT operators with
+      regards to the True/False constants.  An expression like
+      ``col.is_(True)`` will now render ``col IS true``
+      on the target platform, rather than converting the True/
+      False constant to an integer bound parameter.
+      This allows the ``is_()`` operator to work on MySQL when
+      given True/False constants.
+
     .. change::
       :tags: bug, postgresql
       :tickets: 2681
index 28b1c6ddd501f89931c4b3cf1f71437e56f2db89..d2e644ce2b684db3c37b7bacf37d27da9efe7a5a 100644 (file)
@@ -1591,7 +1591,9 @@ def _interpret_as_from(element):
 
 
 def _const_expr(element):
-    if element is None:
+    if isinstance(element, (Null, False_, True_)):
+        return element
+    elif element is None:
         return null()
     elif element is False:
         return false()
@@ -2011,18 +2013,33 @@ class _DefaultColumnComparator(operators.ColumnOperators):
         return op, other_comparator.type
 
     def _boolean_compare(self, expr, op, obj, negate=None, reverse=False,
-                        **kwargs
-        ):
-        if obj is None or isinstance(obj, Null):
-            if op in (operators.eq, operators.is_):
-                return BinaryExpression(expr, null(), operators.is_,
-                        negate=operators.isnot)
-            elif op in (operators.ne, operators.isnot):
-                return BinaryExpression(expr, null(), operators.isnot,
-                        negate=operators.is_)
+                        **kwargs):
+        if isinstance(obj, (util.NoneType, bool, Null, True_, False_)):
+
+            # allow x ==/!= True/False to be treated as a literal.
+            # this comes out to "== / != true/false" or "1/0" if those
+            # constants aren't supported and works on all platforms
+            if op in (operators.eq, operators.ne) and \
+                    isinstance(obj, (bool, True_, False_)):
+                return BinaryExpression(expr,
+                                obj,
+                                op,
+                                type_=sqltypes.BOOLEANTYPE,
+                                negate=negate, modifiers=kwargs)
             else:
-                raise exc.ArgumentError("Only '='/'!=' operators can "
-                        "be used with NULL")
+                # all other None/True/False uses IS, IS NOT
+                if op in (operators.eq, operators.is_):
+                    return BinaryExpression(expr, _const_expr(obj),
+                            operators.is_,
+                            negate=operators.isnot)
+                elif op in (operators.ne, operators.isnot):
+                    return BinaryExpression(expr, _const_expr(obj),
+                            operators.isnot,
+                            negate=operators.is_)
+                else:
+                    raise exc.ArgumentError(
+                        "Only '=', '!=', 'is_()', 'isnot()' operators can "
+                        "be used with None/True/False")
         else:
             obj = self._check_literal(expr, op, obj)
 
@@ -3253,6 +3270,8 @@ class False_(ColumnElement):
     def __init__(self):
         self.type = sqltypes.BOOLEANTYPE
 
+    def compare(self, other):
+        return isinstance(other, False_)
 
 class True_(ColumnElement):
     """Represent the ``true`` keyword in a SQL statement.
@@ -3266,6 +3285,9 @@ class True_(ColumnElement):
     def __init__(self):
         self.type = sqltypes.BOOLEANTYPE
 
+    def compare(self, other):
+        return isinstance(other, True_)
+
 
 class ClauseList(ClauseElement):
     """Describe a list of clauses, separated by an operator.
index 7215ae565221844d45f53e037b2beb6d7edabe53..d1db733e09302d99a9ce29f230bb0718ac651ef2 100644 (file)
@@ -1,7 +1,7 @@
 from sqlalchemy.testing import fixtures, eq_, is_
 from sqlalchemy import testing
 from sqlalchemy.testing import assert_raises_message
-from sqlalchemy.sql import column, desc, asc, literal, collate
+from sqlalchemy.sql import column, desc, asc, literal, collate, null, true, false
 from sqlalchemy.sql.expression import BinaryExpression, \
                 ClauseList, Grouping, \
                 UnaryExpression, select, union, func, tuple_
@@ -66,6 +66,33 @@ class DefaultColumnComparatorTest(fixtures.TestBase):
     def test_isnot_null(self):
         self._do_operate_test(operators.isnot, None)
 
+    def test_is_null_const(self):
+        self._do_operate_test(operators.is_, null())
+
+    def test_is_true_const(self):
+        self._do_operate_test(operators.is_, true())
+
+    def test_is_false_const(self):
+        self._do_operate_test(operators.is_, false())
+
+    def test_equals_true(self):
+        self._do_operate_test(operators.eq, True)
+
+    def test_notequals_true(self):
+        self._do_operate_test(operators.ne, True)
+
+    def test_is_true(self):
+        self._do_operate_test(operators.is_, True)
+
+    def test_isnot_true(self):
+        self._do_operate_test(operators.isnot, True)
+
+    def test_is_false(self):
+        self._do_operate_test(operators.is_, False)
+
+    def test_isnot_false(self):
+        self._do_operate_test(operators.isnot, False)
+
     def test_like(self):
         self._do_operate_test(operators.like_op)