From: Mike Bayer Date: Mon, 22 Apr 2013 23:12:47 +0000 (-0400) Subject: Fully implemented the IS and IS NOT operators with X-Git-Tag: rel_0_8_1~7^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5884c2e7e5b46cee29b90aa3f7161e7380e3e2a5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git 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. [ticket:2682] --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 489cad5824..1f89984527 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -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 diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 28b1c6ddd5..d2e644ce2b 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -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. diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index 7215ae5652..d1db733e09 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -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)