]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add _negate() to Label to negate inner element
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 21 Apr 2017 17:35:38 +0000 (13:35 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 21 Apr 2017 17:35:38 +0000 (13:35 -0400)
Fixed the negation of a :class:`.Label` construct so that the
inner element is negated correctly, when the :func:`.not_` modifier
is applied to the labeled expression.

Change-Id: Ia99917b2959bdfbff28689c962b4203911c57b85
Fixes: #3969
doc/build/changelog/changelog_12.rst
lib/sqlalchemy/sql/elements.py
test/sql/test_operators.py

index 57262ef9b517b5159ce0782ba011ee664e929913..1f49281eddd12df82fc8411fed3cfbd91c969351 100644 (file)
 .. changelog::
     :version: 1.2.0b1
 
+    .. change:: 3969
+        :tags: bug, sql
+        :tickets: 3969
+
+        Fixed the negation of a :class:`.Label` construct so that the
+        inner element is negated correctly, when the :func:`.not_` modifier
+        is applied to the labeled expression.
+
     .. change::
         :tags: bug, orm
         :tickets: 3967
index 414e3f47780b11e6d7911a985bf11db4e28122b6..1ce03e66a3d9b08e5f6842f6ed62ef199dbf3e70 100644 (file)
@@ -3577,7 +3577,13 @@ class Label(ColumnElement):
         return self._element.self_group(against=operators.as_)
 
     def self_group(self, against=None):
-        sub_element = self._element.self_group(against=against)
+        return self._apply_to_inner(self._element.self_group, against=against)
+
+    def _negate(self):
+        return self._apply_to_inner(self._element._negate)
+
+    def _apply_to_inner(self, fn, *arg, **kw):
+        sub_element = fn(*arg, **kw)
         if sub_element is not self._element:
             return Label(self.name,
                          sub_element,
index ac05d3a8114b7bb6c7c198905acfe90b52adc50c..c0637d22571343c1ad49de6c27d1a44c03aeffb8 100644 (file)
@@ -11,7 +11,7 @@ import operator
 from sqlalchemy import String, Integer, LargeBinary
 from sqlalchemy import exc
 from sqlalchemy.engine import default
-from sqlalchemy.sql.elements import _literal_as_text
+from sqlalchemy.sql.elements import _literal_as_text, Label
 from sqlalchemy.schema import Column, Table, MetaData
 from sqlalchemy.sql import compiler
 from sqlalchemy.types import TypeEngine, TypeDecorator, UserDefinedType, \
@@ -2079,6 +2079,20 @@ class NegationTest(fixtures.TestBase, testing.AssertsCompiledSQL):
             self.table1.c.myid.type,
         )
 
+    def test_negate_operator_label(self):
+        orig_expr = or_(
+            self.table1.c.myid == 1, self.table1.c.myid == 2).label('foo')
+        expr = not_(orig_expr)
+        isinstance(expr, Label)
+        eq_(expr.name, 'foo')
+        is_not_(expr, orig_expr)
+        is_(expr._element.operator, operator.inv)  # e.g. and not false_
+
+        self.assert_compile(
+            expr,
+            "NOT (mytable.myid = :myid_1 OR mytable.myid = :myid_2)"
+        )
+
 
 class LikeTest(fixtures.TestBase, testing.AssertsCompiledSQL):
     __dialect__ = 'default'