summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/elements.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
-rw-r--r--lib/sqlalchemy/sql/elements.py344
1 files changed, 199 insertions, 145 deletions
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 691eb10ec..da1d50a53 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -70,12 +70,14 @@ from .visitors import Visitable
from .. import exc
from .. import inspection
from .. import util
-from ..util.langhelpers import TypingOnly
+from ..util import HasMemoized_ro_memoized_attribute
+from ..util import TypingOnly
from ..util.typing import Literal
if typing.TYPE_CHECKING:
- from ._typing import _ColumnExpression
+ from ._typing import _ColumnExpressionArgument
from ._typing import _PropagateAttrsType
+ from ._typing import _SelectIterable
from ._typing import _TypeEngineArgument
from .cache_key import CacheKey
from .compiler import Compiled
@@ -300,7 +302,7 @@ class ClauseElement(
is_clause_element = True
is_selectable = False
-
+ _is_column_element = False
_is_table = False
_is_textual = False
_is_from_clause = False
@@ -330,7 +332,7 @@ class ClauseElement(
) -> Iterable[ClauseElement]:
...
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return []
@@ -696,6 +698,9 @@ class CompilerColumnElement(
__slots__ = ()
+# SQLCoreOperations should be suiting the ExpressionElementRole
+# and ColumnsClauseRole. however the MRO issues become too elaborate
+# at the moment.
class SQLCoreOperations(Generic[_T], ColumnOperators, TypingOnly):
__slots__ = ()
@@ -1154,6 +1159,7 @@ class ColumnElement(
primary_key: bool = False
_is_clone_of: Optional[ColumnElement[_T]]
+ _is_column_element = True
foreign_keys: AbstractSet[ForeignKey] = frozenset()
@@ -1396,7 +1402,7 @@ class ColumnElement(
return self
@property
- def _select_iterable(self) -> Iterable[ColumnElement[Any]]:
+ def _select_iterable(self) -> _SelectIterable:
return (self,)
@util.memoized_property
@@ -2075,7 +2081,7 @@ class TextClause(
return and_(self, other)
@property
- def _select_iterable(self):
+ def _select_iterable(self) -> _SelectIterable:
return (self,)
# help in those cases where text() is
@@ -2491,9 +2497,11 @@ class ClauseList(
("operator", InternalTraversal.dp_operator),
]
+ clauses: List[ColumnElement[Any]]
+
def __init__(
self,
- *clauses: _ColumnExpression[Any],
+ *clauses: _ColumnExpressionArgument[Any],
operator: OperatorType = operators.comma_op,
group: bool = True,
group_contents: bool = True,
@@ -2541,7 +2549,7 @@ class ClauseList(
return len(self.clauses)
@property
- def _select_iterable(self):
+ def _select_iterable(self) -> _SelectIterable:
return itertools.chain.from_iterable(
[elem._select_iterable for elem in self.clauses]
)
@@ -2558,7 +2566,7 @@ class ClauseList(
coercions.expect(self._text_converter_role, clause)
)
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return list(itertools.chain(*[c._from_objects for c in self.clauses]))
@@ -2580,8 +2588,12 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
@classmethod
def _process_clauses_for_boolean(
- cls, operator, continue_on, skip_on, clauses
- ):
+ cls,
+ operator: OperatorType,
+ continue_on: Any,
+ skip_on: Any,
+ clauses: Iterable[ColumnElement[Any]],
+ ) -> typing_Tuple[int, List[ColumnElement[Any]]]:
has_continue_on = None
convert_clauses = []
@@ -2623,9 +2635,9 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
operator: OperatorType,
continue_on: Any,
skip_on: Any,
- *clauses: _ColumnExpression[Any],
+ *clauses: _ColumnExpressionArgument[Any],
**kw: Any,
- ) -> BooleanClauseList:
+ ) -> ColumnElement[Any]:
lcc, convert_clauses = cls._process_clauses_for_boolean(
operator,
continue_on,
@@ -2639,7 +2651,7 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
if lcc > 1:
# multiple elements. Return regular BooleanClauseList
# which will link elements against the operator.
- return cls._construct_raw(operator, convert_clauses) # type: ignore[no-any-return] # noqa E501
+ return cls._construct_raw(operator, convert_clauses) # type: ignore # noqa E501
elif lcc == 1:
# just one element. return it as a single boolean element,
# not a list and discard the operator.
@@ -2663,7 +2675,9 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
return cls._construct_raw(operator) # type: ignore[no-any-return] # noqa E501
@classmethod
- def _construct_for_whereclause(cls, clauses):
+ def _construct_for_whereclause(
+ cls, clauses: Iterable[ColumnElement[Any]]
+ ) -> Optional[ColumnElement[bool]]:
operator, continue_on, skip_on = (
operators.and_,
True_._singleton,
@@ -2689,7 +2703,11 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
return None
@classmethod
- def _construct_raw(cls, operator, clauses=None):
+ def _construct_raw(
+ cls,
+ operator: OperatorType,
+ clauses: Optional[List[ColumnElement[Any]]] = None,
+ ) -> BooleanClauseList:
self = cls.__new__(cls)
self.clauses = clauses if clauses else []
self.group = True
@@ -2700,7 +2718,9 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
return self
@classmethod
- def and_(cls, *clauses: _ColumnExpression[bool]) -> BooleanClauseList:
+ def and_(
+ cls, *clauses: _ColumnExpressionArgument[bool]
+ ) -> ColumnElement[bool]:
r"""Produce a conjunction of expressions joined by ``AND``.
See :func:`_sql.and_` for full documentation.
@@ -2710,7 +2730,9 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
)
@classmethod
- def or_(cls, *clauses: _ColumnExpression[bool]) -> BooleanClauseList:
+ def or_(
+ cls, *clauses: _ColumnExpressionArgument[bool]
+ ) -> ColumnElement[bool]:
"""Produce a conjunction of expressions joined by ``OR``.
See :func:`_sql.or_` for full documentation.
@@ -2720,7 +2742,7 @@ class BooleanClauseList(ClauseList, ColumnElement[bool]):
)
@property
- def _select_iterable(self):
+ def _select_iterable(self) -> _SelectIterable:
return (self,)
def self_group(self, against=None):
@@ -2751,7 +2773,7 @@ class Tuple(ClauseList, ColumnElement[typing_Tuple[Any, ...]]):
@util.preload_module("sqlalchemy.sql.sqltypes")
def __init__(
self,
- *clauses: _ColumnExpression[Any],
+ *clauses: _ColumnExpressionArgument[Any],
types: Optional[Sequence[_TypeEngineArgument[Any]]] = None,
):
sqltypes = util.preloaded.sql_sqltypes
@@ -2780,7 +2802,7 @@ class Tuple(ClauseList, ColumnElement[typing_Tuple[Any, ...]]):
super(Tuple, self).__init__(*init_clauses)
@property
- def _select_iterable(self):
+ def _select_iterable(self) -> _SelectIterable:
return (self,)
def _bind_param(self, operator, obj, type_=None, expanding=False):
@@ -2856,7 +2878,8 @@ class Case(ColumnElement[_T]):
def __init__(
self,
*whens: Union[
- typing_Tuple[_ColumnExpression[bool], Any], Mapping[Any, Any]
+ typing_Tuple[_ColumnExpressionArgument[bool], Any],
+ Mapping[Any, Any],
],
value: Optional[Any] = None,
else_: Optional[Any] = None,
@@ -2900,7 +2923,7 @@ class Case(ColumnElement[_T]):
else:
self.else_ = None
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return list(
itertools.chain(*[x._from_objects for x in self.get_children()])
@@ -2944,7 +2967,7 @@ class Cast(WrapsColumnExpression[_T]):
def __init__(
self,
- expression: _ColumnExpression[Any],
+ expression: _ColumnExpressionArgument[Any],
type_: _TypeEngineArgument[_T],
):
self.type = type_api.to_instance(type_)
@@ -2956,7 +2979,7 @@ class Cast(WrapsColumnExpression[_T]):
)
self.typeclause = TypeClause(self.type)
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.clause._from_objects
@@ -2995,7 +3018,7 @@ class TypeCoerce(WrapsColumnExpression[_T]):
def __init__(
self,
- expression: _ColumnExpression[Any],
+ expression: _ColumnExpressionArgument[Any],
type_: _TypeEngineArgument[_T],
):
self.type = type_api.to_instance(type_)
@@ -3006,7 +3029,7 @@ class TypeCoerce(WrapsColumnExpression[_T]):
apply_propagate_attrs=self,
)
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.clause._from_objects
@@ -3044,12 +3067,12 @@ class Extract(ColumnElement[int]):
expr: ColumnElement[Any]
field: str
- def __init__(self, field: str, expr: _ColumnExpression[Any]):
+ def __init__(self, field: str, expr: _ColumnExpressionArgument[Any]):
self.type = type_api.INTEGERTYPE
self.field = field
self.expr = coercions.expect(roles.ExpressionElementRole, expr)
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.expr._from_objects
@@ -3076,7 +3099,7 @@ class _label_reference(ColumnElement[_T]):
def __init__(self, element: ColumnElement[_T]):
self.element = element
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return []
@@ -3142,7 +3165,7 @@ class UnaryExpression(ColumnElement[_T]):
@classmethod
def _create_nulls_first(
cls,
- column: _ColumnExpression[_T],
+ column: _ColumnExpressionArgument[_T],
) -> UnaryExpression[_T]:
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
@@ -3153,7 +3176,7 @@ class UnaryExpression(ColumnElement[_T]):
@classmethod
def _create_nulls_last(
cls,
- column: _ColumnExpression[_T],
+ column: _ColumnExpressionArgument[_T],
) -> UnaryExpression[_T]:
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
@@ -3163,7 +3186,7 @@ class UnaryExpression(ColumnElement[_T]):
@classmethod
def _create_desc(
- cls, column: _ColumnExpression[_T]
+ cls, column: _ColumnExpressionArgument[_T]
) -> UnaryExpression[_T]:
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
@@ -3174,7 +3197,7 @@ class UnaryExpression(ColumnElement[_T]):
@classmethod
def _create_asc(
cls,
- column: _ColumnExpression[_T],
+ column: _ColumnExpressionArgument[_T],
) -> UnaryExpression[_T]:
return UnaryExpression(
coercions.expect(roles.ByOfRole, column),
@@ -3185,7 +3208,7 @@ class UnaryExpression(ColumnElement[_T]):
@classmethod
def _create_distinct(
cls,
- expr: _ColumnExpression[_T],
+ expr: _ColumnExpressionArgument[_T],
) -> UnaryExpression[_T]:
col_expr = coercions.expect(roles.ExpressionElementRole, expr)
return UnaryExpression(
@@ -3202,7 +3225,7 @@ class UnaryExpression(ColumnElement[_T]):
else:
return None
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.element._from_objects
@@ -3238,7 +3261,7 @@ class CollectionAggregate(UnaryExpression[_T]):
@classmethod
def _create_any(
- cls, expr: _ColumnExpression[_T]
+ cls, expr: _ColumnExpressionArgument[_T]
) -> CollectionAggregate[bool]:
col_expr = coercions.expect(
roles.ExpressionElementRole,
@@ -3254,7 +3277,7 @@ class CollectionAggregate(UnaryExpression[_T]):
@classmethod
def _create_all(
- cls, expr: _ColumnExpression[_T]
+ cls, expr: _ColumnExpressionArgument[_T]
) -> CollectionAggregate[bool]:
col_expr = coercions.expect(
roles.ExpressionElementRole,
@@ -3431,7 +3454,7 @@ class BinaryExpression(ColumnElement[_T]):
def is_comparison(self):
return operators.is_comparison(self.operator)
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.left._from_objects + self.right._from_objects
@@ -3557,7 +3580,7 @@ class Grouping(GroupedElement, ColumnElement[_T]):
else:
return []
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.element._from_objects
@@ -3614,10 +3637,16 @@ class Over(ColumnElement[_T]):
self,
element: ColumnElement[_T],
partition_by: Optional[
- Union[Iterable[_ColumnExpression[Any]], _ColumnExpression[Any]]
+ Union[
+ Iterable[_ColumnExpressionArgument[Any]],
+ _ColumnExpressionArgument[Any],
+ ]
] = None,
order_by: Optional[
- Union[Iterable[_ColumnExpression[Any]], _ColumnExpression[Any]]
+ Union[
+ Iterable[_ColumnExpressionArgument[Any]],
+ _ColumnExpressionArgument[Any],
+ ]
] = None,
range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
@@ -3697,7 +3726,7 @@ class Over(ColumnElement[_T]):
def type(self):
return self.element.type
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return list(
itertools.chain(
@@ -3737,7 +3766,9 @@ class WithinGroup(ColumnElement[_T]):
order_by: Optional[ClauseList] = None
def __init__(
- self, element: FunctionElement[_T], *order_by: _ColumnExpression[Any]
+ self,
+ element: FunctionElement[_T],
+ *order_by: _ColumnExpressionArgument[Any],
):
self.element = element
if order_by is not None:
@@ -3774,7 +3805,7 @@ class WithinGroup(ColumnElement[_T]):
else:
return self.element.type
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return list(
itertools.chain(
@@ -3817,7 +3848,9 @@ class FunctionFilter(ColumnElement[_T]):
criterion: Optional[ColumnElement[bool]] = None
def __init__(
- self, func: FunctionElement[_T], *criterion: _ColumnExpression[bool]
+ self,
+ func: FunctionElement[_T],
+ *criterion: _ColumnExpressionArgument[bool],
):
self.func = func
self.filter(*criterion)
@@ -3847,10 +3880,16 @@ class FunctionFilter(ColumnElement[_T]):
def over(
self,
partition_by: Optional[
- Union[Iterable[_ColumnExpression[Any]], _ColumnExpression[Any]]
+ Union[
+ Iterable[_ColumnExpressionArgument[Any]],
+ _ColumnExpressionArgument[Any],
+ ]
] = None,
order_by: Optional[
- Union[Iterable[_ColumnExpression[Any]], _ColumnExpression[Any]]
+ Union[
+ Iterable[_ColumnExpressionArgument[Any]],
+ _ColumnExpressionArgument[Any],
+ ]
] = None,
range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None,
@@ -3890,7 +3929,7 @@ class FunctionFilter(ColumnElement[_T]):
def type(self):
return self.func.type
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return list(
itertools.chain(
@@ -3903,7 +3942,97 @@ class FunctionFilter(ColumnElement[_T]):
)
-class Label(roles.LabeledColumnExprRole[_T], ColumnElement[_T]):
+class NamedColumn(ColumnElement[_T]):
+ is_literal = False
+ table: Optional[FromClause] = None
+ name: str
+ key: str
+
+ def _compare_name_for_result(self, other):
+ return (hasattr(other, "name") and self.name == other.name) or (
+ hasattr(other, "_label") and self._label == other._label
+ )
+
+ @util.ro_memoized_property
+ def description(self) -> str:
+ return self.name
+
+ @HasMemoized.memoized_attribute
+ def _tq_key_label(self):
+ """table qualified label based on column key.
+
+ for table-bound columns this is <tablename>_<column key/proxy key>;
+
+ all other expressions it resolves to key/proxy key.
+
+ """
+ proxy_key = self._proxy_key
+ if proxy_key and proxy_key != self.name:
+ return self._gen_tq_label(proxy_key)
+ else:
+ return self._tq_label
+
+ @HasMemoized.memoized_attribute
+ def _tq_label(self) -> Optional[str]:
+ """table qualified label based on column name.
+
+ for table-bound columns this is <tablename>_<columnname>; all other
+ expressions it resolves to .name.
+
+ """
+ return self._gen_tq_label(self.name)
+
+ @HasMemoized.memoized_attribute
+ def _render_label_in_columns_clause(self):
+ return True
+
+ @HasMemoized.memoized_attribute
+ def _non_anon_label(self):
+ return self.name
+
+ def _gen_tq_label(
+ self, name: str, dedupe_on_key: bool = True
+ ) -> Optional[str]:
+ return name
+
+ def _bind_param(self, operator, obj, type_=None, expanding=False):
+ return BindParameter(
+ self.key,
+ obj,
+ _compared_to_operator=operator,
+ _compared_to_type=self.type,
+ type_=type_,
+ unique=True,
+ expanding=expanding,
+ )
+
+ def _make_proxy(
+ self,
+ selectable,
+ name=None,
+ name_is_truncatable=False,
+ disallow_is_literal=False,
+ **kw,
+ ):
+ c = ColumnClause(
+ coercions.expect(roles.TruncatedLabelRole, name or self.name)
+ if name_is_truncatable
+ else (name or self.name),
+ type_=self.type,
+ _selectable=selectable,
+ is_literal=False,
+ )
+
+ c._propagate_attrs = selectable._propagate_attrs
+ if name is None:
+ c.key = self.key
+ c._proxies = [self]
+ if selectable._is_clone_of is not None:
+ c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
+ return c.key, c
+
+
+class Label(roles.LabeledColumnExprRole[_T], NamedColumn[_T]):
"""Represents a column label (AS).
Represent a label, as typically applied to any column-level
@@ -3925,7 +4054,7 @@ class Label(roles.LabeledColumnExprRole[_T], ColumnElement[_T]):
def __init__(
self,
name: Optional[str],
- element: _ColumnExpression[_T],
+ element: _ColumnExpressionArgument[_T],
type_: Optional[_TypeEngineArgument[_T]] = None,
):
orig_element = element
@@ -3964,6 +4093,21 @@ class Label(roles.LabeledColumnExprRole[_T], ColumnElement[_T]):
def __reduce__(self):
return self.__class__, (self.name, self._element, self.type)
+ @HasMemoized.memoized_attribute
+ def _render_label_in_columns_clause(self):
+ return True
+
+ def _bind_param(self, operator, obj, type_=None, expanding=False):
+ return BindParameter(
+ None,
+ obj,
+ _compared_to_operator=operator,
+ type_=type_,
+ _compared_to_type=self.type,
+ unique=True,
+ expanding=expanding,
+ )
+
@util.memoized_property
def _is_implicitly_boolean(self):
return self.element._is_implicitly_boolean
@@ -4010,7 +4154,7 @@ class Label(roles.LabeledColumnExprRole[_T], ColumnElement[_T]):
)
self.key = self._tq_label = self._tq_key_label = self.name
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return self.element._from_objects
@@ -4047,96 +4191,6 @@ class Label(roles.LabeledColumnExprRole[_T], ColumnElement[_T]):
return self.key, e
-class NamedColumn(ColumnElement[_T]):
- is_literal = False
- table: Optional[FromClause] = None
- name: str
- key: str
-
- def _compare_name_for_result(self, other):
- return (hasattr(other, "name") and self.name == other.name) or (
- hasattr(other, "_label") and self._label == other._label
- )
-
- @util.ro_memoized_property
- def description(self) -> str:
- return self.name
-
- @HasMemoized.memoized_attribute
- def _tq_key_label(self):
- """table qualified label based on column key.
-
- for table-bound columns this is <tablename>_<column key/proxy key>;
-
- all other expressions it resolves to key/proxy key.
-
- """
- proxy_key = self._proxy_key
- if proxy_key and proxy_key != self.name:
- return self._gen_tq_label(proxy_key)
- else:
- return self._tq_label
-
- @HasMemoized.memoized_attribute
- def _tq_label(self) -> Optional[str]:
- """table qualified label based on column name.
-
- for table-bound columns this is <tablename>_<columnname>; all other
- expressions it resolves to .name.
-
- """
- return self._gen_tq_label(self.name)
-
- @HasMemoized.memoized_attribute
- def _render_label_in_columns_clause(self):
- return True
-
- @HasMemoized.memoized_attribute
- def _non_anon_label(self):
- return self.name
-
- def _gen_tq_label(
- self, name: str, dedupe_on_key: bool = True
- ) -> Optional[str]:
- return name
-
- def _bind_param(self, operator, obj, type_=None, expanding=False):
- return BindParameter(
- self.key,
- obj,
- _compared_to_operator=operator,
- _compared_to_type=self.type,
- type_=type_,
- unique=True,
- expanding=expanding,
- )
-
- def _make_proxy(
- self,
- selectable,
- name=None,
- name_is_truncatable=False,
- disallow_is_literal=False,
- **kw,
- ):
- c = ColumnClause(
- coercions.expect(roles.TruncatedLabelRole, name or self.name)
- if name_is_truncatable
- else (name or self.name),
- type_=self.type,
- _selectable=selectable,
- is_literal=False,
- )
-
- c._propagate_attrs = selectable._propagate_attrs
- if name is None:
- c.key = self.key
- c._proxies = [self]
- if selectable._is_clone_of is not None:
- c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
- return c.key, c
-
-
class ColumnClause(
roles.DDLReferredColumnRole,
roles.LabeledColumnExprRole[_T],
@@ -4242,7 +4296,7 @@ class ColumnClause(
return super(ColumnClause, self)._clone(**kw)
- @HasMemoized.memoized_attribute
+ @HasMemoized_ro_memoized_attribute
def _from_objects(self) -> List[FromClause]:
t = self.table
if t is not None:
@@ -4395,7 +4449,7 @@ class TableValuedColumn(NamedColumn[_T]):
self.scalar_alias = clone(self.scalar_alias, **kw)
self.key = self.name = self.scalar_alias.name
- @util.non_memoized_property
+ @util.ro_non_memoized_property
def _from_objects(self) -> List[FromClause]:
return [self.scalar_alias]
@@ -4409,7 +4463,7 @@ class CollationClause(ColumnElement[str]):
@classmethod
def _create_collation_expression(
- cls, expression: _ColumnExpression[str], collation: str
+ cls, expression: _ColumnExpressionArgument[str], collation: str
) -> BinaryExpression[str]:
expr = coercions.expect(roles.ExpressionElementRole, expression)
return BinaryExpression(