__slots__ = ()
_propagate_attrs = util.EMPTY_DICT
+ _is_collection_aggregate = False
# SQLCoreOperations should be suiting the ExpressionElementRole
_is_column_element = True
_insert_sentinel: bool = False
_omit_from_statements = False
+ _is_collection_aggregate = False
foreign_keys: AbstractSet[ForeignKey] = frozenset()
_omit_from_statements = False
+ _is_collection_aggregate = False
+
@property
def _hide_froms(self) -> Iterable[FromClause]:
return ()
*(left_flattened + right_flattened),
)
+ if right._is_collection_aggregate:
+ negate = None
+
return BinaryExpression(
left, right, op, type_=type_, negate=negate, modifiers=modifiers
)
"""
inherit_cache = True
+ _is_collection_aggregate = True
@classmethod
def _create_any(
raise exc.ArgumentError(
"Only comparison operators may be used with ANY/ALL"
)
- kwargs["reverse"] = kwargs["_any_all_expr"] = True
+ kwargs["reverse"] = True
return self.comparator.operate(operators.mirror(op), *other, **kwargs)
def reverse_operate(self, op, other, **kwargs):
modifiers=self.modifiers,
)
else:
- return super()._negate()
+ return self.self_group()._negate()
class Slice(ColumnElement[Any]):
def any(self, other, operator=None):
"""Return ``other operator ANY (array)`` clause.
- .. note:: This method is an :class:`_types.ARRAY` - specific
+ .. legacy:: This method is an :class:`_types.ARRAY` - specific
construct that is now superseded by the :func:`_sql.any_`
function, which features a different calling style. The
:func:`_sql.any_` function is also mirrored at the method level
arr_type = self.type
- # send plain BinaryExpression so that negate remains at None,
- # leading to NOT expr for negation.
- return elements.BinaryExpression(
+ return elements.CollectionAggregate._create_any(self.expr).operate(
+ operators.mirror(operator),
coercions.expect(
roles.BinaryElementRole,
element=other,
expr=self.expr,
bindparam_type=arr_type.item_type,
),
- elements.CollectionAggregate._create_any(self.expr),
- operator,
)
@util.preload_module("sqlalchemy.sql.elements")
def all(self, other, operator=None):
"""Return ``other operator ALL (array)`` clause.
- .. note:: This method is an :class:`_types.ARRAY` - specific
- construct that is now superseded by the :func:`_sql.any_`
+ .. legacy:: This method is an :class:`_types.ARRAY` - specific
+ construct that is now superseded by the :func:`_sql.all_`
function, which features a different calling style. The
- :func:`_sql.any_` function is also mirrored at the method level
- via the :meth:`_sql.ColumnOperators.any_` method.
+ :func:`_sql.all_` function is also mirrored at the method level
+ via the :meth:`_sql.ColumnOperators.all_` method.
Usage of array-specific :meth:`_types.ARRAY.Comparator.all`
is as follows::
arr_type = self.type
- # send plain BinaryExpression so that negate remains at None,
- # leading to NOT expr for negation.
- return elements.BinaryExpression(
+ return elements.CollectionAggregate._create_all(self.expr).operate(
+ operators.mirror(operator),
coercions.expect(
roles.BinaryElementRole,
element=other,
expr=self.expr,
bindparam_type=arr_type.item_type,
),
- elements.CollectionAggregate._create_all(self.expr),
- operator,
)
comparator_factory = Comparator
)
return t
- @testing.combinations(
+ null_comparisons = testing.combinations(
lambda col: any_(col) == None,
lambda col: col.any_() == None,
lambda col: any_(col) == null(),
lambda col: None == col.any_(),
argnames="expr",
)
+
+ @null_comparisons
@testing.combinations("int", "array", argnames="datatype")
def test_any_generic_null(self, datatype, expr, t_fixture):
col = t_fixture.c.data if datatype == "int" else t_fixture.c.arrval
self.assert_compile(expr(col), "NULL = ANY (tab1.%s)" % col.name)
+ @null_comparisons
+ @testing.combinations("int", "array", argnames="datatype")
+ def test_any_generic_null_negate(self, datatype, expr, t_fixture):
+ col = t_fixture.c.data if datatype == "int" else t_fixture.c.arrval
+
+ self.assert_compile(
+ ~expr(col), "NOT (NULL = ANY (tab1.%s))" % col.name
+ )
+
@testing.fixture(
params=[
("ANY", any_),
("ALL", lambda x: x.all_()),
]
)
- def operator(self, request):
+ def any_all_operators(self, request):
return request.param
+ # test legacy array any() / all(). these are superseded by the
+ # any_() / all_() versions
@testing.fixture(
params=[
("ANY", lambda x, *o: x.any(*o)),
("ALL", lambda x, *o: x.all(*o)),
]
)
- def array_op(self, request):
+ def legacy_any_all_operators(self, request):
return request.param
- def test_array(self, t_fixture, operator):
+ def test_array(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
self.assert_compile(
5 == fn(t.c.arrval),
f":param_1 = {op} (tab1.arrval)",
checkparams={"param_1": 5},
)
- def test_comparator_array(self, t_fixture, operator):
+ def test_comparator_inline_negate(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
+ self.assert_compile(
+ 5 != fn(t.c.arrval),
+ f":param_1 != {op} (tab1.arrval)",
+ checkparams={"param_1": 5},
+ )
+
+ @testing.combinations(
+ (operator.eq, "="),
+ (operator.ne, "!="),
+ (operator.gt, ">"),
+ (operator.le, "<="),
+ argnames="operator,opstring",
+ )
+ def test_comparator_outer_negate(
+ self, t_fixture, any_all_operators, operator, opstring
+ ):
+ """test #10817"""
+ t = t_fixture
+ op, fn = any_all_operators
+ self.assert_compile(
+ ~(operator(5, fn(t.c.arrval))),
+ f"NOT (:param_1 {opstring} {op} (tab1.arrval))",
+ checkparams={"param_1": 5},
+ )
+
+ def test_comparator_array(self, t_fixture, any_all_operators):
+ t = t_fixture
+ op, fn = any_all_operators
self.assert_compile(
5 > fn(t.c.arrval),
f":param_1 > {op} (tab1.arrval)",
checkparams={"param_1": 5},
)
- def test_comparator_array_wexpr(self, t_fixture, operator):
+ def test_comparator_array_wexpr(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
self.assert_compile(
t.c.data > fn(t.c.arrval),
f"tab1.data > {op} (tab1.arrval)",
checkparams={},
)
- def test_illegal_ops(self, t_fixture, operator):
+ def test_illegal_ops(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
assert_raises_message(
exc.ArgumentError,
t.c.data + fn(t.c.arrval), f"tab1.data + {op} (tab1.arrval)"
)
- def test_bindparam_coercion(self, t_fixture, array_op):
+ def test_bindparam_coercion(self, t_fixture, legacy_any_all_operators):
"""test #7979"""
t = t_fixture
- op, fn = array_op
+ op, fn = legacy_any_all_operators
expr = fn(t.c.arrval, bindparam("param"))
expected = f"%(param)s = {op} (tab1.arrval)"
self.assert_compile(expr, expected, dialect="postgresql")
- def test_array_comparator_accessor(self, t_fixture, array_op):
+ def test_array_comparator_accessor(
+ self, t_fixture, legacy_any_all_operators
+ ):
t = t_fixture
- op, fn = array_op
+ op, fn = legacy_any_all_operators
self.assert_compile(
fn(t.c.arrval, 5, operator.gt),
checkparams={"arrval_1": 5},
)
- def test_array_comparator_negate_accessor(self, t_fixture, array_op):
+ def test_array_comparator_negate_accessor(
+ self, t_fixture, legacy_any_all_operators
+ ):
t = t_fixture
- op, fn = array_op
+ op, fn = legacy_any_all_operators
self.assert_compile(
~fn(t.c.arrval, 5, operator.gt),
checkparams={"arrval_1": 5},
)
- def test_array_expression(self, t_fixture, operator):
+ def test_array_expression(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
self.assert_compile(
5 == fn(t.c.arrval[5:6] + postgresql.array([3, 4])),
dialect="postgresql",
)
- def test_subq(self, t_fixture, operator):
+ def test_subq(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
self.assert_compile(
5 == fn(select(t.c.data).where(t.c.data < 10).scalar_subquery()),
checkparams={"data_1": 10, "param_1": 5},
)
- def test_scalar_values(self, t_fixture, operator):
+ def test_scalar_values(self, t_fixture, any_all_operators):
t = t_fixture
- op, fn = operator
+ op, fn = any_all_operators
self.assert_compile(
5 == fn(values(t.c.data).data([(1,), (42,)]).scalar_values()),