summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/__init__.py1
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py3
-rw-r--r--lib/sqlalchemy/sql/_elements_constructors.py17
-rw-r--r--lib/sqlalchemy/sql/compiler.py7
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py16
-rw-r--r--lib/sqlalchemy/sql/elements.py15
-rw-r--r--lib/sqlalchemy/sql/expression.py1
-rw-r--r--lib/sqlalchemy/sql/operators.py120
8 files changed, 180 insertions, 0 deletions
diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py
index 447451f4b..9cb8140c6 100644
--- a/lib/sqlalchemy/__init__.py
+++ b/lib/sqlalchemy/__init__.py
@@ -89,6 +89,7 @@ from .sql.expression import between as between
from .sql.expression import BinaryExpression as BinaryExpression
from .sql.expression import bindparam as bindparam
from .sql.expression import BindParameter as BindParameter
+from .sql.expression import bitwise_not as bitwise_not
from .sql.expression import BooleanClauseList as BooleanClauseList
from .sql.expression import CacheKey as CacheKey
from .sql.expression import Case as Case
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index d7e6634b3..255c72042 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -1730,6 +1730,9 @@ class PGCompiler(compiler.SQLCompiler):
self.process(element.stop, **kw),
)
+ def visit_bitwise_xor_op_binary(self, binary, operator, **kw):
+ return self._generate_generic_binary(binary, " # ", **kw)
+
def visit_json_getitem_op_binary(
self, binary, operator, _cast_applied=False, **kw
):
diff --git a/lib/sqlalchemy/sql/_elements_constructors.py b/lib/sqlalchemy/sql/_elements_constructors.py
index 9b9632273..98f5a1cc6 100644
--- a/lib/sqlalchemy/sql/_elements_constructors.py
+++ b/lib/sqlalchemy/sql/_elements_constructors.py
@@ -1030,6 +1030,23 @@ def distinct(expr: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]:
return UnaryExpression._create_distinct(expr)
+def bitwise_not(expr: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]:
+ """Produce a unary bitwise NOT clause, typically via the ``~`` operator.
+
+ Not to be confused with boolean negation :func:`_sql.not_`.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+
+ """
+
+ return UnaryExpression._create_bitwise_not(expr)
+
+
def extract(field: str, expr: _ColumnExpressionArgument[Any]) -> Extract:
"""Return a :class:`.Extract` construct.
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index d4ddc2e5d..bc463f9a1 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -279,6 +279,13 @@ OPERATORS = {
operators.asc_op: " ASC",
operators.nulls_first_op: " NULLS FIRST",
operators.nulls_last_op: " NULLS LAST",
+ # bitwise
+ operators.bitwise_xor_op: " ^ ",
+ operators.bitwise_or_op: " | ",
+ operators.bitwise_and_op: " & ",
+ operators.bitwise_not_op: "~",
+ operators.bitwise_lshift_op: " << ",
+ operators.bitwise_rshift_op: " >> ",
}
FUNCTIONS: Dict[Type[Function[Any]], str] = {
diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py
index 19b5291c4..57460b036 100644
--- a/lib/sqlalchemy/sql/default_comparator.py
+++ b/lib/sqlalchemy/sql/default_comparator.py
@@ -273,6 +273,16 @@ def _neg_impl(
return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
+def _bitwise_not_impl(
+ expr: ColumnElement[Any], op: OperatorType, **kw: Any
+) -> ColumnElement[Any]:
+ """See :meth:`.ColumnOperators.bitwise_not`."""
+
+ return UnaryExpression(
+ expr, operator=operators.bitwise_not_op, type_=expr.type
+ )
+
+
def _match_impl(
expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
) -> ColumnElement[Any]:
@@ -420,6 +430,12 @@ operator_lookup: Dict[
"sub": (_binary_operate, util.EMPTY_DICT),
"div": (_binary_operate, util.EMPTY_DICT),
"mod": (_binary_operate, util.EMPTY_DICT),
+ "bitwise_xor_op": (_binary_operate, util.EMPTY_DICT),
+ "bitwise_or_op": (_binary_operate, util.EMPTY_DICT),
+ "bitwise_and_op": (_binary_operate, util.EMPTY_DICT),
+ "bitwise_not_op": (_bitwise_not_impl, util.EMPTY_DICT),
+ "bitwise_lshift_op": (_binary_operate, util.EMPTY_DICT),
+ "bitwise_rshift_op": (_binary_operate, util.EMPTY_DICT),
"truediv": (_binary_operate, util.EMPTY_DICT),
"floordiv": (_binary_operate, util.EMPTY_DICT),
"custom_op": (_custom_op_operate, util.EMPTY_DICT),
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index c3f7b884c..70c65b5a1 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -3587,6 +3587,21 @@ class UnaryExpression(ColumnElement[_T]):
wraps_column_expression=False,
)
+ @classmethod
+ def _create_bitwise_not(
+ cls,
+ expr: _ColumnExpressionArgument[_T],
+ ) -> UnaryExpression[_T]:
+ col_expr: ColumnElement[_T] = coercions.expect(
+ roles.ExpressionElementRole, expr
+ )
+ return UnaryExpression(
+ col_expr,
+ operator=operators.bitwise_not_op,
+ type_=col_expr.type,
+ wraps_column_expression=False,
+ )
+
@property
def _order_by_label_element(self) -> Optional[Label[Any]]:
if self.modifier in (operators.desc_op, operators.asc_op):
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 3f5c76a50..7076cd10d 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -22,6 +22,7 @@ from ._elements_constructors import any_ as any_
from ._elements_constructors import asc as asc
from ._elements_constructors import between as between
from ._elements_constructors import bindparam as bindparam
+from ._elements_constructors import bitwise_not as bitwise_not
from ._elements_constructors import case as case
from ._elements_constructors import cast as cast
from ._elements_constructors import collate as collate
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index 567802916..c973126ca 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -697,6 +697,90 @@ class ColumnOperators(Operators):
"""
return self.operate(ilike_op, other, escape=escape)
+ def bitwise_xor(self, other: Any) -> ColumnOperators:
+ """Produce a bitwise XOR operation, typically via the ``^``
+ operator, or ``#`` for PostgreSQL.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_xor_op, other)
+
+ def bitwise_or(self, other: Any) -> ColumnOperators:
+ """Produce a bitwise OR operation, typically via the ``|``
+ operator.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_or_op, other)
+
+ def bitwise_and(self, other: Any) -> ColumnOperators:
+ """Produce a bitwise AND operation, typically via the ``&``
+ operator.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_and_op, other)
+
+ def bitwise_not(self) -> ColumnOperators:
+ """Produce a bitwise NOT operation, typically via the ``~``
+ operator.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_not_op)
+
+ def bitwise_lshift(self, other: Any) -> ColumnOperators:
+ """Produce a bitwise LSHIFT operation, typically via the ``<<``
+ operator.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_lshift_op, other)
+
+ def bitwise_rshift(self, other: Any) -> ColumnOperators:
+ """Produce a bitwise RSHIFT operation, typically via the ``>>``
+ operator.
+
+ .. versionadded:: 2.0.2
+
+ .. seealso::
+
+ :ref:`operators_bitwise`
+
+ """
+
+ return self.operate(bitwise_rshift_op, other)
+
def in_(self, other: Any) -> ColumnOperators:
"""Implement the ``in`` operator.
@@ -2266,6 +2350,36 @@ def json_path_getitem_op(a: Any, b: Any) -> Any:
raise NotImplementedError()
+@_operator_fn
+def bitwise_xor_op(a: Any, b: Any) -> Any:
+ return a.bitwise_xor(b)
+
+
+@_operator_fn
+def bitwise_or_op(a: Any, b: Any) -> Any:
+ return a.bitwise_or(b)
+
+
+@_operator_fn
+def bitwise_and_op(a: Any, b: Any) -> Any:
+ return a.bitwise_and(b)
+
+
+@_operator_fn
+def bitwise_not_op(a: Any) -> Any:
+ return a.bitwise_not()
+
+
+@_operator_fn
+def bitwise_lshift_op(a: Any, b: Any) -> Any:
+ return a.bitwise_lshift(b)
+
+
+@_operator_fn
+def bitwise_rshift_op(a: Any, b: Any) -> Any:
+ return a.bitwise_rshift(b)
+
+
def is_comparison(op: OperatorType) -> bool:
return op in _comparison or isinstance(op, custom_op) and op.is_comparison
@@ -2344,8 +2458,14 @@ _PRECEDENCE: Dict[OperatorType, int] = {
floordiv: 8,
mod: 8,
neg: 8,
+ bitwise_not_op: 8,
add: 7,
sub: 7,
+ bitwise_xor_op: 7,
+ bitwise_or_op: 7,
+ bitwise_and_op: 7,
+ bitwise_lshift_op: 7,
+ bitwise_rshift_op: 7,
concat_op: 6,
filter_op: 6,
match_op: 5,