]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed issue where inadvertent use of the Python ``__contains__``
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Feb 2016 15:15:40 +0000 (10:15 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Feb 2016 15:41:01 +0000 (10:41 -0500)
override with a column expression (e.g. by using ``'x' in col``)
would cause an endless loop in the case of an ARRAY type, as Python
defers this to ``__getitem__`` access which never raises for this
type.  Overall, all use of ``__contains__`` now raises
NotImplementedError.
fixes #3642

(cherry picked from commit e0a580b3d055a600afae61840058a5a30ef5fe74)

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/sql/default_comparator.py
lib/sqlalchemy/sql/operators.py
test/dialect/postgresql/test_types.py
test/sql/test_operators.py

index 781911d6505c9792162089f0fc2257b581eb9b38..320cb7f2af00938f595e2de81fa40b4c676aa133 100644 (file)
     :version: 1.0.12
     :released:
 
+    .. change::
+        :tags: bug, sql
+        :tickets: 3642
+
+        Fixed issue where inadvertent use of the Python ``__contains__``
+        override with a column expression (e.g. by using ``'x' in col``)
+        would cause an endless loop in the case of an ARRAY type, as Python
+        defers this to ``__getitem__`` access which never raises for this
+        type.  Overall, all use of ``__contains__`` now raises
+        NotImplementedError.
+
     .. change::
         :tags: bug, engine, mysql
         :tickets: 2696
index 305f838938e4a26c0f630fd8337603fd2e0813c0..d180dbc02eee8ff206a4d047983e3e16ce96afb7 100644 (file)
@@ -263,6 +263,7 @@ operator_lookup = {
     "getitem": (_unsupported_impl,),
     "lshift": (_unsupported_impl,),
     "rshift": (_unsupported_impl,),
+    "contains": (_unsupported_impl,),
 }
 
 
index 05be71e9c22eaf423189d1f08c5f80951731fcff..5e2900d8c27cf8089f637fc01dfd50065a496371 100644 (file)
@@ -15,7 +15,7 @@ from .. import util
 
 from operator import (
     and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg,
-    getitem, lshift, rshift
+    getitem, lshift, rshift, contains
 )
 
 if util.py2k:
@@ -333,6 +333,9 @@ class ColumnOperators(Operators):
         """
         return self.operate(neg)
 
+    def __contains__(self, other):
+        return self.operate(contains, other)
+
     def __getitem__(self, index):
         """Implement the [] operator.
 
index 2ab0eee8189bcb9f3ff1afa9b994486b60c7e70a..7e098019d4ed1f168daa36ef4b54dc9cae85fb22 100644 (file)
@@ -707,7 +707,6 @@ class TimePrecisionTest(fixtures.TestBase, AssertsCompiledSQL):
 
 
 class ArrayTest(fixtures.TablesTest, AssertsExecutionResults):
-
     __only_on__ = 'postgresql'
     __backend__ = True
     __unsupported_on__ = 'postgresql+pg8000', 'postgresql+zxjdbc'
@@ -803,6 +802,15 @@ class ArrayTest(fixtures.TablesTest, AssertsExecutionResults):
         eq_(len(results), 1)
         eq_(results[0][0], 5)
 
+    def test_contains_override_raises(self):
+        col = Column('x', postgresql.ARRAY(Integer))
+
+        assert_raises_message(
+            NotImplementedError,
+            "Operator 'contains' is not supported on this expression",
+            lambda: 'foo' in col
+        )
+
     def test_array_subtype_resultprocessor(self):
         arrtable = self.tables.arrtable
         arrtable.insert().execute(intarr=[4, 5, 6],
index bb4cb1bf17220d46a6b0731be99d9f6e955afe17..1a0554e5071324be705ea702236d8664473c4859 100644 (file)
@@ -13,7 +13,7 @@ from sqlalchemy.engine import default
 from sqlalchemy.sql.elements import _literal_as_text
 from sqlalchemy.schema import Column, Table, MetaData
 from sqlalchemy.types import TypeEngine, TypeDecorator, UserDefinedType, \
-    Boolean, NullType, MatchType
+    Boolean, NullType, MatchType, DateTime
 from sqlalchemy.dialects import mysql, firebird, postgresql, oracle, \
     sqlite, mssql
 from sqlalchemy import util
@@ -210,6 +210,18 @@ class DefaultColumnComparatorTest(fixtures.TestBase):
     def test_concat(self):
         self._do_operate_test(operators.concat_op)
 
+    def test_contains_override_raises(self):
+        for col in [
+            Column('x', String),
+            Column('x', Integer),
+            Column('x', DateTime)
+        ]:
+            assert_raises_message(
+                NotImplementedError,
+                "Operator 'contains' is not supported on this expression",
+                lambda: 'foo' in col
+            )
+
 
 class CustomUnaryOperatorTest(fixtures.TestBase, testing.AssertsCompiledSQL):
     __dialect__ = 'default'