diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-04-22 19:13:00 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-04-22 19:13:00 -0400 |
| commit | 600b4bc1a2ac093af5797ecd8717bbc038f1e5c1 (patch) | |
| tree | ed8a37934eb00d4c01bfeec117ea7b1bb980db68 | |
| parent | b6c523d25550cfa01cbe7e9dd24dd1179a32eae8 (diff) | |
| parent | 5884c2e7e5b46cee29b90aa3f7161e7380e3e2a5 (diff) | |
| download | sqlalchemy-600b4bc1a2ac093af5797ecd8717bbc038f1e5c1.tar.gz | |
merge default
| -rw-r--r-- | doc/build/changelog/changelog_08.rst | 12 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/expression.py | 46 | ||||
| -rw-r--r-- | test/sql/test_operators.py | 29 |
3 files changed, 74 insertions, 13 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 489cad582..1f8998452 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -7,6 +7,18 @@ :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 28b1c6ddd..d2e644ce2 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 7215ae565..d1db733e0 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) |
