diff options
-rw-r--r-- | doc/build/changelog/changelog_12.rst | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/default_comparator.py | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/operators.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/sqltypes.py | 4 | ||||
-rw-r--r-- | test/sql/test_operators.py | 10 |
5 files changed, 32 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst index b87682b6d..2b6741494 100644 --- a/doc/build/changelog/changelog_12.rst +++ b/doc/build/changelog/changelog_12.rst @@ -13,6 +13,17 @@ .. changelog:: :version: 1.2.0b1 + .. change:: 3873 + :tags: bug, sql + :tickets: 3873 + + Repaired issue where the type of an expression that used + :meth:`.ColumnOperators.is_` or similar would not be a "boolean" type, + instead the type would be "nulltype", as well as when using custom + comparison operators against an untyped expression. This typing can + impact how the expression behaves in larger contexts as well as + in result-row-handling. + .. change:: 3969 :tags: bug, sql :tickets: 3969 diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py index 4ba53ef75..4485c661b 100644 --- a/lib/sqlalchemy/sql/default_comparator.py +++ b/lib/sqlalchemy/sql/default_comparator.py @@ -50,11 +50,15 @@ def _boolean_compare(expr, op, obj, negate=None, reverse=False, if op in (operators.eq, operators.is_): return BinaryExpression(expr, _const_expr(obj), operators.is_, - negate=operators.isnot) + negate=operators.isnot, + type_=result_type + ) elif op in (operators.ne, operators.isnot): return BinaryExpression(expr, _const_expr(obj), operators.isnot, - negate=operators.is_) + negate=operators.is_, + type_=result_type + ) else: raise exc.ArgumentError( "Only '=', '!=', 'is_()', 'isnot()', " diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py index 49642acdd..58f32b3e6 100644 --- a/lib/sqlalchemy/sql/operators.py +++ b/lib/sqlalchemy/sql/operators.py @@ -1021,7 +1021,8 @@ def json_path_getitem_op(a, b): _commutative = {eq, ne, add, mul} -_comparison = {eq, ne, lt, gt, ge, le, between_op, like_op} +_comparison = {eq, ne, lt, gt, ge, le, between_op, like_op, is_, + isnot, is_distinct_from, isnot_distinct_from} def is_comparison(op): diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index b8117e3ca..b8c8c8116 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -2555,7 +2555,9 @@ class NullType(TypeEngine): class Comparator(TypeEngine.Comparator): def _adapt_expression(self, op, other_comparator): - if isinstance(other_comparator, NullType.Comparator) or \ + if operators.is_comparison(op): + return op, BOOLEANTYPE + elif isinstance(other_comparator, NullType.Comparator) or \ not operators.is_commutative(op): return op, self.expr.type else: diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index c0637d225..3dd9af5e2 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -3,6 +3,7 @@ from sqlalchemy import testing from sqlalchemy.testing import assert_raises_message from sqlalchemy.sql import column, desc, asc, literal, collate, null, \ true, false, any_, all_ +from sqlalchemy.sql import sqltypes from sqlalchemy.sql.expression import BinaryExpression, \ ClauseList, Grouping, \ UnaryExpression, select, union, func, tuple_ @@ -62,6 +63,12 @@ class DefaultColumnComparatorTest(fixtures.TestBase): self._loop_test(operator, right) + if operators.is_comparison(operator): + is_( + left.comparator.operate(operator, right).type, + sqltypes.BOOLEANTYPE + ) + def _loop_test(self, operator, *arg): loop = LoopOperate() is_( @@ -2617,6 +2624,9 @@ class CustomOpTest(fixtures.TestBase): assert operators.is_comparison(op1) assert not operators.is_comparison(op2) + expr = c.op('$', is_comparison=True)(None) + is_(expr.type, sqltypes.BOOLEANTYPE) + class TupleTypingTest(fixtures.TestBase): |