summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2017-03-14 19:39:37 -0400
committerGerrit Code Review <gerrit@awstats.zzzcomputing.com>2017-03-14 19:39:37 -0400
commit0a35ccc6bd1acaa91f32c8e0140000b19ae85e14 (patch)
tree55ef032cad4685ff8645e349fd5709524a57ce36 /lib/sqlalchemy/sql
parent18b2dea9a48ad282a7af32633b913b5855e6f100 (diff)
parentf3b6f4f8da5223fae0a1dd948d4266b2e49e317c (diff)
downloadsqlalchemy-0a35ccc6bd1acaa91f32c8e0140000b19ae85e14.tar.gz
Merge "Add "empty in" strategies; default to "static""
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/compiler.py24
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py23
-rw-r--r--lib/sqlalchemy/sql/operators.py32
3 files changed, 62 insertions, 17 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index bfa22c206..a1d5a879d 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -1003,6 +1003,30 @@ class SQLCompiler(Compiled):
return "NOT %s" % self.visit_binary(
binary, override_operator=operators.match_op)
+ def _emit_empty_in_warning(self):
+ util.warn(
+ 'The IN-predicate was invoked with an '
+ 'empty sequence. This results in a '
+ 'contradiction, which nonetheless can be '
+ 'expensive to evaluate. Consider alternative '
+ 'strategies for improved performance.')
+
+ def visit_empty_in_op_binary(self, binary, operator, **kw):
+ if self.dialect._use_static_in:
+ return "1 != 1"
+ else:
+ if self.dialect._warn_on_empty_in:
+ self._emit_empty_in_warning()
+ return self.process(binary.left != binary.left)
+
+ def visit_empty_notin_op_binary(self, binary, operator, **kw):
+ if self.dialect._use_static_in:
+ return "1 = 1"
+ else:
+ if self.dialect._warn_on_empty_in:
+ self._emit_empty_in_warning()
+ return self.process(binary.left == binary.left)
+
def visit_binary(self, binary, override_operator=None,
eager_grouping=False, **kw):
diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py
index 7498bbe5d..d409ebacc 100644
--- a/lib/sqlalchemy/sql/default_comparator.py
+++ b/lib/sqlalchemy/sql/default_comparator.py
@@ -146,23 +146,14 @@ def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
else:
o = expr._bind_param(op, o)
args.append(o)
- if len(args) == 0:
- # 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.' % expr)
- if op is operators.in_op:
- return expr != expr
- else:
- return expr == expr
+ if len(args) == 0:
+ op, negate_op = (
+ operators.empty_in_op,
+ operators.empty_notin_op) if op is operators.in_op \
+ else (
+ operators.empty_notin_op,
+ operators.empty_in_op)
return _boolean_compare(expr, op,
ClauseList(*args).self_group(against=op),
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index 1690d546b..01bee62cf 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -458,6 +458,17 @@ class ColumnOperators(Operators):
"other" may be a tuple/list of column expressions,
or a :func:`~.expression.select` construct.
+ In the case that ``other`` is an empty sequence, the compiler
+ produces an "empty in" expression. This defaults to the
+ expression "1 != 1" to produce false in all cases. The
+ :paramref:`.create_engine.empty_in_strategy` may be used to
+ alter this behavior.
+
+ .. versionchanged:: 1.2 The :meth:`.ColumnOperators.in_` and
+ :meth:`.ColumnOperators.notin_` operators
+ now produce a "static" expression for an empty IN sequence
+ by default.
+
"""
return self.operate(in_op, other)
@@ -467,7 +478,16 @@ class ColumnOperators(Operators):
This is equivalent to using negation with
:meth:`.ColumnOperators.in_`, i.e. ``~x.in_(y)``.
- .. versionadded:: 0.8
+ In the case that ``other`` is an empty sequence, the compiler
+ produces an "empty not in" expression. This defaults to the
+ expression "1 = 1" to produce true in all cases. The
+ :paramref:`.create_engine.empty_in_strategy` may be used to
+ alter this behavior.
+
+ .. versionchanged:: 1.2 The :meth:`.ColumnOperators.in_` and
+ :meth:`.ColumnOperators.notin_` operators
+ now produce a "static" expression for an empty IN sequence
+ by default.
.. seealso::
@@ -957,6 +977,14 @@ def comma_op(a, b):
raise NotImplementedError()
+def empty_in_op(a, b):
+ raise NotImplementedError()
+
+
+def empty_notin_op(a, b):
+ raise NotImplementedError()
+
+
def concat_op(a, b):
return a.concat(b)
@@ -1073,6 +1101,8 @@ _PRECEDENCE = {
ne: 5,
is_distinct_from: 5,
isnot_distinct_from: 5,
+ empty_in_op: 5,
+ empty_notin_op: 5,
gt: 5,
lt: 5,
ge: 5,