summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2009-12-29 23:20:48 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2009-12-29 23:20:48 +0000
commitd732e7bf261504f34c9b9d5049a9875633c3e4b3 (patch)
treeef497b8dad9036a7a8b1e8c230870555010d8b0f
parent8c3c2ea508894bc3e18c1df26364a74a4f68107a (diff)
downloadsqlalchemy-d732e7bf261504f34c9b9d5049a9875633c3e4b3.tar.gz
- calling expr.in_([]), i.e. with an empty list, emits a warning
before issuing the usual "expr != expr" clause. The "expr != expr" can be very expensive, and it's preferred that the user not issue in_() if the list is empty, instead simply not querying, or modifying the criterion as appropriate for more complex situations. [ticket:1628]
-rw-r--r--CHANGES10
-rw-r--r--lib/sqlalchemy/sql/expression.py10
-rw-r--r--test/sql/test_query.py3
-rw-r--r--test/sql/test_select.py1
4 files changed, 22 insertions, 2 deletions
diff --git a/CHANGES b/CHANGES
index 66ecdbf08..ae6e83959 100644
--- a/CHANGES
+++ b/CHANGES
@@ -260,7 +260,15 @@ CHANGES
named "tablename_id" - this is because
the labeling logic is always applied to all columns
so a naming conflict will never occur.
-
+
+ - calling expr.in_([]), i.e. with an empty list, emits a warning
+ before issuing the usual "expr != expr" clause. The
+ "expr != expr" can be very expensive, and it's preferred
+ that the user not issue in_() if the list is empty,
+ instead simply not querying, or modifying the criterion
+ as appropriate for more complex situations.
+ [ticket:1628]
+
- Deprecated or removed:
* "scalar" flag on select() is removed, use
select.as_scalar().
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 2f0ac90af..acfb3d7c4 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -1498,7 +1498,15 @@ class _CompareMixin(ColumnOperators):
args.append(o)
if len(args) == 0:
- # Special case handling for empty IN's, behave like comparison against zero row selectable
+ # Special case handling for empty IN's, behave like comparison
+ # against zero row selectable. We use != to build the
+ # contradiction as it handles NULL values appropriately, i.e.
+ # "not (x IN ())" should not return NULL values for x.
+ util.warn("The IN-predicate on \"%s\" was invoked with an empty sequence. "
+ "This results in a contradiction, which nonetheless can be "
+ "expensive to evaluate. Consider alternative strategies for "
+ "improved performance." % self)
+
return self != self
return self.__compare(op, ClauseList(*args).self_group(against=op), negate=negate_op)
diff --git a/test/sql/test_query.py b/test/sql/test_query.py
index 0e1787e9e..c711819f9 100644
--- a/test/sql/test_query.py
+++ b/test/sql/test_query.py
@@ -721,6 +721,7 @@ class QueryTest(TestBase):
finally:
shadowed.drop(checkfirst=True)
+ @testing.emits_warning('.*empty sequence.*')
def test_in_filtering(self):
"""test the behavior of the in_() function."""
@@ -747,6 +748,7 @@ class QueryTest(TestBase):
# Null values are not outside any set
assert len(r) == 0
+ @testing.emits_warning('.*empty sequence.*')
@testing.fails_on('firebird', "kinterbasdb doesn't send full type information")
def test_bind_in(self):
users.insert().execute(user_id = 7, user_name = 'jack')
@@ -761,6 +763,7 @@ class QueryTest(TestBase):
r = s.execute(search_key=None).fetchall()
assert len(r) == 0
+ @testing.emits_warning('.*empty sequence.*')
@testing.fails_on('firebird', 'FIXME: unknown')
@testing.fails_on('maxdb', 'FIXME: unknown')
@testing.fails_on('oracle', 'FIXME: unknown')
diff --git a/test/sql/test_select.py b/test/sql/test_select.py
index 42998ee02..d063bd2d9 100644
--- a/test/sql/test_select.py
+++ b/test/sql/test_select.py
@@ -1265,6 +1265,7 @@ UNION SELECT mytable.myid FROM mytable"
assert [str(c) for c in s.c] == ["id", "hoho"]
+ @testing.emits_warning('.*empty sequence.*')
def test_in(self):
self.assert_compile(select([table1], table1.c.myid.in_(['a'])),
"SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1)")