1# sql/elements.py
2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: allow-untyped-defs, allow-untyped-calls
8
9"""Core SQL expression elements, including :class:`_expression.ClauseElement`,
10:class:`_expression.ColumnElement`, and derived classes.
11
12"""
13
14from __future__ import annotations
15
16from decimal import Decimal
17from enum import Enum
18import itertools
19import operator
20import re
21import typing
22from typing import AbstractSet
23from typing import Any
24from typing import Callable
25from typing import cast
26from typing import Dict
27from typing import FrozenSet
28from typing import Generic
29from typing import Iterable
30from typing import Iterator
31from typing import List
32from typing import Literal
33from typing import Mapping
34from typing import Optional
35from typing import overload
36from typing import ParamSpec
37from typing import Sequence
38from typing import Set
39from typing import Tuple as typing_Tuple
40from typing import Type
41from typing import TYPE_CHECKING
42from typing import TypeVar
43from typing import Union
44
45from . import coercions
46from . import operators
47from . import roles
48from . import traversals
49from . import type_api
50from ._typing import has_schema_attr
51from ._typing import is_named_from_clause
52from ._typing import is_quoted_name
53from ._typing import is_tuple_type
54from .annotation import Annotated
55from .annotation import SupportsWrappingAnnotations
56from .base import _clone
57from .base import _expand_cloned
58from .base import _generative
59from .base import _NoArg
60from .base import Executable
61from .base import ExecutableStatement
62from .base import Generative
63from .base import HasMemoized
64from .base import Immutable
65from .base import NO_ARG
66from .base import SingletonConstant
67from .cache_key import MemoizedHasCacheKey
68from .cache_key import NO_CACHE
69from .coercions import _document_text_coercion # noqa
70from .operators import ColumnOperators
71from .operators import OperatorClass
72from .traversals import HasCopyInternals
73from .visitors import cloned_traverse
74from .visitors import ExternallyTraversible
75from .visitors import InternalTraversal
76from .visitors import traverse
77from .visitors import Visitable
78from .. import exc
79from .. import inspection
80from .. import util
81from ..util import deprecated
82from ..util import HasMemoized_ro_memoized_attribute
83from ..util import TypingOnly
84from ..util.compat import Template
85from ..util.typing import Self
86from ..util.typing import TupleAny
87from ..util.typing import Unpack
88
89
90if typing.TYPE_CHECKING:
91 from ._typing import _ByArgument
92 from ._typing import _ColumnExpressionArgument
93 from ._typing import _ColumnExpressionOrStrLabelArgument
94 from ._typing import _HasDialect
95 from ._typing import _InfoType
96 from ._typing import _OnlyColumnArgument
97 from ._typing import _PropagateAttrsType
98 from ._typing import _TypeEngineArgument
99 from .base import _EntityNamespace
100 from .base import ColumnSet
101 from .cache_key import _CacheKeyTraversalType
102 from .cache_key import CacheKey
103 from .compiler import Compiled
104 from .compiler import SQLCompiler
105 from .functions import FunctionElement
106 from .operators import OperatorType
107 from .schema import Column
108 from .schema import DefaultGenerator
109 from .schema import FetchedValue
110 from .schema import ForeignKey
111 from .selectable import _SelectIterable
112 from .selectable import FromClause
113 from .selectable import NamedFromClause
114 from .selectable import TextualSelect
115 from .sqltypes import TupleType
116 from .type_api import TypeEngine
117 from .visitors import _CloneCallableType
118 from .visitors import _TraverseInternalsType
119 from .visitors import anon_map
120 from ..engine import Connection
121 from ..engine import Dialect
122 from ..engine.interfaces import _CoreMultiExecuteParams
123 from ..engine.interfaces import _CoreSingleExecuteParams
124 from ..engine.interfaces import CacheStats
125 from ..engine.interfaces import CompiledCacheType
126 from ..engine.interfaces import CoreExecuteOptionsParameter
127 from ..engine.interfaces import SchemaTranslateMapType
128 from ..engine.result import Result
129
130
131_NUMERIC = Union[float, Decimal]
132_NUMBER = Union[float, int, Decimal]
133
134_T = TypeVar("_T", bound="Any")
135_T_co = TypeVar("_T_co", bound=Any, covariant=True)
136_OPT = TypeVar("_OPT", bound="Any")
137_NT = TypeVar("_NT", bound="_NUMERIC")
138
139_NMT = TypeVar("_NMT", bound="_NUMBER")
140
141
142@overload
143def literal(
144 value: Any,
145 type_: _TypeEngineArgument[_T],
146 literal_execute: bool = False,
147) -> BindParameter[_T]: ...
148
149
150@overload
151def literal(
152 value: _T,
153 type_: None = None,
154 literal_execute: bool = False,
155) -> BindParameter[_T]: ...
156
157
158@overload
159def literal(
160 value: Any,
161 type_: Optional[_TypeEngineArgument[Any]] = None,
162 literal_execute: bool = False,
163) -> BindParameter[Any]: ...
164
165
166def literal(
167 value: Any,
168 type_: Optional[_TypeEngineArgument[Any]] = None,
169 literal_execute: bool = False,
170) -> BindParameter[Any]:
171 r"""Return a literal clause, bound to a bind parameter.
172
173 Literal clauses are created automatically when non-
174 :class:`_expression.ClauseElement` objects (such as strings, ints, dates,
175 etc.) are
176 used in a comparison operation with a :class:`_expression.ColumnElement`
177 subclass,
178 such as a :class:`~sqlalchemy.schema.Column` object. Use this function
179 to force the generation of a literal clause, which will be created as a
180 :class:`BindParameter` with a bound value.
181
182 :param value: the value to be bound. Can be any Python object supported by
183 the underlying DB-API, or is translatable via the given type argument.
184
185 :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` which will
186 provide bind-parameter translation for this literal.
187
188 :param literal_execute: optional bool, when True, the SQL engine will
189 attempt to render the bound value directly in the SQL statement at
190 execution time rather than providing as a parameter value.
191
192 .. versionadded:: 2.0
193
194 """
195 return coercions.expect(
196 roles.LiteralValueRole,
197 value,
198 type_=type_,
199 literal_execute=literal_execute,
200 )
201
202
203def literal_column(
204 text: str, type_: Optional[_TypeEngineArgument[_T]] = None
205) -> ColumnClause[_T]:
206 r"""Produce a :class:`.ColumnClause` object that has the
207 :paramref:`_expression.column.is_literal` flag set to True.
208
209 :func:`_expression.literal_column` is similar to
210 :func:`_expression.column`, except that
211 it is more often used as a "standalone" column expression that renders
212 exactly as stated; while :func:`_expression.column`
213 stores a string name that
214 will be assumed to be part of a table and may be quoted as such,
215 :func:`_expression.literal_column` can be that,
216 or any other arbitrary column-oriented
217 expression.
218
219 :param text: the text of the expression; can be any SQL expression.
220 Quoting rules will not be applied. To specify a column-name expression
221 which should be subject to quoting rules, use the :func:`column`
222 function.
223
224 :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine`
225 object which will
226 provide result-set translation and additional expression semantics for
227 this column. If left as ``None`` the type will be :class:`.NullType`.
228
229 .. seealso::
230
231 :func:`_expression.column`
232
233 :func:`_expression.text`
234
235 :ref:`tutorial_select_arbitrary_text`
236
237 """
238 return ColumnClause(text, type_=type_, is_literal=True)
239
240
241class CompilerElement(Visitable):
242 """base class for SQL elements that can be compiled to produce a
243 SQL string.
244
245 .. versionadded:: 2.0
246
247 """
248
249 __slots__ = ()
250 __visit_name__ = "compiler_element"
251
252 supports_execution = False
253
254 stringify_dialect = "default"
255
256 @util.preload_module("sqlalchemy.engine.default")
257 @util.preload_module("sqlalchemy.engine.url")
258 def compile(
259 self,
260 bind: Optional[_HasDialect] = None,
261 dialect: Optional[Dialect] = None,
262 **kw: Any,
263 ) -> Compiled:
264 """Compile this SQL expression.
265
266 The return value is a :class:`~.Compiled` object.
267 Calling ``str()`` or ``unicode()`` on the returned value will yield a
268 string representation of the result. The
269 :class:`~.Compiled` object also can return a
270 dictionary of bind parameter names and values
271 using the ``params`` accessor.
272
273 :param bind: An :class:`.Connection` or :class:`.Engine` which
274 can provide a :class:`.Dialect` in order to generate a
275 :class:`.Compiled` object. If the ``bind`` and
276 ``dialect`` parameters are both omitted, a default SQL compiler
277 is used.
278
279 :param column_keys: Used for INSERT and UPDATE statements, a list of
280 column names which should be present in the VALUES clause of the
281 compiled statement. If ``None``, all columns from the target table
282 object are rendered.
283
284 :param dialect: A :class:`.Dialect` instance which can generate
285 a :class:`.Compiled` object. This argument takes precedence over
286 the ``bind`` argument.
287
288 :param compile_kwargs: optional dictionary of additional parameters
289 that will be passed through to the compiler within all "visit"
290 methods. This allows any custom flag to be passed through to
291 a custom compilation construct, for example. It is also used
292 for the case of passing the ``literal_binds`` flag through::
293
294 from sqlalchemy.sql import table, column, select
295
296 t = table("t", column("x"))
297
298 s = select(t).where(t.c.x == 5)
299
300 print(s.compile(compile_kwargs={"literal_binds": True}))
301
302 .. seealso::
303
304 :ref:`faq_sql_expression_string`
305
306 """
307
308 if dialect is None:
309 if bind:
310 dialect = bind.dialect
311 elif self.stringify_dialect == "default":
312 dialect = self._default_dialect()
313 else:
314 url = util.preloaded.engine_url
315 dialect = url.URL.create(
316 self.stringify_dialect
317 ).get_dialect()()
318
319 return self._compiler(dialect, **kw)
320
321 def _default_dialect(self):
322 default = util.preloaded.engine_default
323 return default.StrCompileDialect()
324
325 def _compiler(self, dialect: Dialect, **kw: Any) -> Compiled:
326 """Return a compiler appropriate for this ClauseElement, given a
327 Dialect."""
328
329 if TYPE_CHECKING:
330 assert isinstance(self, ClauseElement)
331 return dialect.statement_compiler(dialect, self, **kw)
332
333 def __str__(self) -> str:
334 return str(self.compile())
335
336
337@inspection._self_inspects
338class ClauseElement(
339 SupportsWrappingAnnotations,
340 MemoizedHasCacheKey,
341 HasCopyInternals,
342 ExternallyTraversible,
343 CompilerElement,
344):
345 """Base class for elements of a programmatically constructed SQL
346 expression.
347
348 """
349
350 __visit_name__ = "clause"
351
352 if TYPE_CHECKING:
353
354 @util.memoized_property
355 def _propagate_attrs(self) -> _PropagateAttrsType:
356 """like annotations, however these propagate outwards liberally
357 as SQL constructs are built, and are set up at construction time.
358
359 """
360 ...
361
362 else:
363 _propagate_attrs = util.EMPTY_DICT
364
365 @util.ro_memoized_property
366 def description(self) -> Optional[str]:
367 return None
368
369 _is_clone_of: Optional[Self] = None
370
371 is_clause_element = True
372 is_selectable = False
373 is_dml = False
374 _is_column_element = False
375 _is_keyed_column_element = False
376 _is_table = False
377 _gen_static_annotations_cache_key = False
378 _is_textual = False
379 _is_from_clause = False
380 _is_returns_rows = False
381 _is_text_clause = False
382 _is_from_container = False
383 _is_select_container = False
384 _is_select_base = False
385 _is_select_statement = False
386 _is_bind_parameter = False
387 _is_clause_list = False
388 _is_lambda_element = False
389 _is_singleton_constant = False
390 _is_immutable = False
391 _is_star = False
392
393 @property
394 def _order_by_label_element(self) -> Optional[Label[Any]]:
395 return None
396
397 _cache_key_traversal: _CacheKeyTraversalType = None
398
399 negation_clause: ColumnElement[bool]
400
401 if typing.TYPE_CHECKING:
402
403 def get_children(
404 self, *, omit_attrs: typing_Tuple[str, ...] = ..., **kw: Any
405 ) -> Iterable[ClauseElement]: ...
406
407 @util.ro_non_memoized_property
408 def _from_objects(self) -> List[FromClause]:
409 return []
410
411 def _set_propagate_attrs(self, values: Mapping[str, Any]) -> Self:
412 # usually, self._propagate_attrs is empty here. one case where it's
413 # not is a subquery against ORM select, that is then pulled as a
414 # property of an aliased class. should all be good
415
416 # assert not self._propagate_attrs
417
418 self._propagate_attrs = util.immutabledict(values)
419 return self
420
421 def _default_compiler(self) -> SQLCompiler:
422 dialect = self._default_dialect()
423 return dialect.statement_compiler(dialect, self) # type: ignore
424
425 def _clone(self, **kw: Any) -> Self:
426 """Create a shallow copy of this ClauseElement.
427
428 This method may be used by a generative API. Its also used as
429 part of the "deep" copy afforded by a traversal that combines
430 the _copy_internals() method.
431
432 """
433
434 skip = self._memoized_keys
435 c = self.__class__.__new__(self.__class__)
436
437 if skip:
438 # ensure this iteration remains atomic
439 c.__dict__ = {
440 k: v for k, v in self.__dict__.copy().items() if k not in skip
441 }
442 else:
443 c.__dict__ = self.__dict__.copy()
444
445 # this is a marker that helps to "equate" clauses to each other
446 # when a Select returns its list of FROM clauses. the cloning
447 # process leaves around a lot of remnants of the previous clause
448 # typically in the form of column expressions still attached to the
449 # old table.
450 cc = self._is_clone_of
451 c._is_clone_of = cc if cc is not None else self
452 return c
453
454 def _negate_in_binary(self, negated_op, original_op):
455 """a hook to allow the right side of a binary expression to respond
456 to a negation of the binary expression.
457
458 Used for the special case of expanding bind parameter with IN.
459
460 """
461 return self
462
463 def _with_binary_element_type(self, type_):
464 """in the context of binary expression, convert the type of this
465 object to the one given.
466
467 applies only to :class:`_expression.ColumnElement` classes.
468
469 """
470 return self
471
472 @property
473 def _constructor(self): # type: ignore[override]
474 """return the 'constructor' for this ClauseElement.
475
476 This is for the purposes for creating a new object of
477 this type. Usually, its just the element's __class__.
478 However, the "Annotated" version of the object overrides
479 to return the class of its proxied element.
480
481 """
482 return self.__class__
483
484 @HasMemoized.memoized_attribute
485 def _cloned_set(self):
486 """Return the set consisting all cloned ancestors of this
487 ClauseElement.
488
489 Includes this ClauseElement. This accessor tends to be used for
490 FromClause objects to identify 'equivalent' FROM clauses, regardless
491 of transformative operations.
492
493 """
494 s = util.column_set()
495 f: Optional[ClauseElement] = self
496
497 # note this creates a cycle, asserted in test_memusage. however,
498 # turning this into a plain @property adds tends of thousands of method
499 # calls to Core / ORM performance tests, so the small overhead
500 # introduced by the relatively small amount of short term cycles
501 # produced here is preferable
502 while f is not None:
503 s.add(f)
504 f = f._is_clone_of
505 return s
506
507 def _de_clone(self):
508 while self._is_clone_of is not None:
509 self = self._is_clone_of
510 return self
511
512 @util.ro_non_memoized_property
513 def entity_namespace(self) -> _EntityNamespace:
514 raise AttributeError(
515 "This SQL expression has no entity namespace "
516 "with which to filter from."
517 )
518
519 def __getstate__(self):
520 d = self.__dict__.copy()
521 d.pop("_is_clone_of", None)
522 d.pop("_generate_cache_key", None)
523 return d
524
525 def _execute_on_connection(
526 self,
527 connection: Connection,
528 distilled_params: _CoreMultiExecuteParams,
529 execution_options: CoreExecuteOptionsParameter,
530 ) -> Result[Unpack[TupleAny]]:
531 if self.supports_execution:
532 if TYPE_CHECKING:
533 assert isinstance(self, Executable)
534 return connection._execute_clauseelement(
535 self, distilled_params, execution_options
536 )
537 else:
538 raise exc.ObjectNotExecutableError(self)
539
540 def _execute_on_scalar(
541 self,
542 connection: Connection,
543 distilled_params: _CoreMultiExecuteParams,
544 execution_options: CoreExecuteOptionsParameter,
545 ) -> Any:
546 """an additional hook for subclasses to provide a different
547 implementation for connection.scalar() vs. connection.execute().
548
549 .. versionadded:: 2.0
550
551 """
552 return self._execute_on_connection(
553 connection, distilled_params, execution_options
554 ).scalar()
555
556 def _get_embedded_bindparams(self) -> Sequence[BindParameter[Any]]:
557 """Return the list of :class:`.BindParameter` objects embedded in the
558 object.
559
560 This accomplishes the same purpose as ``visitors.traverse()`` or
561 similar would provide, however by making use of the cache key
562 it takes advantage of memoization of the key to result in fewer
563 net method calls, assuming the statement is also going to be
564 executed.
565
566 """
567
568 key = self._generate_cache_key()
569 if key is None:
570 bindparams: List[BindParameter[Any]] = []
571
572 traverse(self, {}, {"bindparam": bindparams.append})
573 return bindparams
574
575 else:
576 return key.bindparams
577
578 def unique_params(
579 self,
580 __optionaldict: Optional[Dict[str, Any]] = None,
581 /,
582 **kwargs: Any,
583 ) -> Self:
584 """Return a copy with :func:`_expression.bindparam` elements
585 replaced.
586
587 Same functionality as :meth:`_expression.ClauseElement.params`,
588 except adds `unique=True`
589 to affected bind parameters so that multiple statements can be
590 used.
591
592 """
593 return self._replace_params(True, __optionaldict, kwargs)
594
595 def params(
596 self,
597 __optionaldict: Optional[Mapping[str, Any]] = None,
598 /,
599 **kwargs: Any,
600 ) -> Self:
601 """Return a copy with :func:`_expression.bindparam` elements
602 replaced.
603
604 Returns a copy of this ClauseElement with
605 :func:`_expression.bindparam`
606 elements replaced with values taken from the given dictionary::
607
608 >>> clause = column("x") + bindparam("foo")
609 >>> print(clause.compile().params)
610 {'foo':None}
611 >>> print(clause.params({"foo": 7}).compile().params)
612 {'foo':7}
613
614 """
615 return self._replace_params(False, __optionaldict, kwargs)
616
617 @deprecated(
618 "2.1",
619 "The params() and unique_params() methods on non-statement "
620 "ClauseElement objects are deprecated; params() is now limited to "
621 "statement level objects such as select(), insert(), union(), etc. ",
622 )
623 def _replace_params(
624 self,
625 unique: bool,
626 optionaldict: Optional[Mapping[str, Any]],
627 kwargs: Dict[str, Any],
628 ) -> Self:
629 if optionaldict:
630 kwargs.update(optionaldict)
631
632 def visit_bindparam(bind: BindParameter[Any]) -> None:
633 if bind.key in kwargs:
634 bind.value = kwargs[bind.key]
635 bind.required = False
636 if unique:
637 bind._convert_to_unique()
638
639 return cloned_traverse(
640 self,
641 {"maintain_key": True, "detect_subquery_cols": True},
642 {"bindparam": visit_bindparam},
643 )
644
645 def compare(self, other: ClauseElement, **kw: Any) -> bool:
646 r"""Compare this :class:`_expression.ClauseElement` to
647 the given :class:`_expression.ClauseElement`.
648
649 Subclasses should override the default behavior, which is a
650 straight identity comparison.
651
652 \**kw are arguments consumed by subclass ``compare()`` methods and
653 may be used to modify the criteria for comparison
654 (see :class:`_expression.ColumnElement`).
655
656 """
657 return traversals.compare(self, other, **kw)
658
659 def self_group(
660 self, against: Optional[OperatorType] = None
661 ) -> ClauseElement:
662 """Apply a 'grouping' to this :class:`_expression.ClauseElement`.
663
664 This method is overridden by subclasses to return a "grouping"
665 construct, i.e. parenthesis. In particular it's used by "binary"
666 expressions to provide a grouping around themselves when placed into a
667 larger expression, as well as by :func:`_expression.select`
668 constructs when placed into the FROM clause of another
669 :func:`_expression.select`. (Note that subqueries should be
670 normally created using the :meth:`_expression.Select.alias` method,
671 as many
672 platforms require nested SELECT statements to be named).
673
674 As expressions are composed together, the application of
675 :meth:`self_group` is automatic - end-user code should never
676 need to use this method directly. Note that SQLAlchemy's
677 clause constructs take operator precedence into account -
678 so parenthesis might not be needed, for example, in
679 an expression like ``x OR (y AND z)`` - AND takes precedence
680 over OR.
681
682 The base :meth:`self_group` method of
683 :class:`_expression.ClauseElement`
684 just returns self.
685 """
686 return self
687
688 def _ungroup(self) -> ClauseElement:
689 """Return this :class:`_expression.ClauseElement`
690 without any groupings.
691 """
692
693 return self
694
695 def _compile_w_cache(
696 self,
697 dialect: Dialect,
698 *,
699 compiled_cache: Optional[CompiledCacheType],
700 column_keys: List[str],
701 for_executemany: bool = False,
702 schema_translate_map: Optional[SchemaTranslateMapType] = None,
703 **kw: Any,
704 ) -> tuple[
705 Compiled,
706 Sequence[BindParameter[Any]] | None,
707 _CoreSingleExecuteParams | None,
708 CacheStats,
709 ]:
710 elem_cache_key: Optional[CacheKey]
711
712 if compiled_cache is not None and dialect._supports_statement_cache:
713 elem_cache_key = self._generate_cache_key()
714 else:
715 elem_cache_key = None
716
717 extracted_params: Optional[Sequence[BindParameter[Any]]]
718 if elem_cache_key is not None:
719 if TYPE_CHECKING:
720 assert compiled_cache is not None
721
722 cache_key, extracted_params, param_dict = elem_cache_key
723 key = (
724 dialect,
725 cache_key,
726 tuple(column_keys),
727 bool(schema_translate_map),
728 for_executemany,
729 )
730 compiled_sql = compiled_cache.get(key)
731
732 if compiled_sql is None:
733 cache_hit = dialect.CACHE_MISS
734 compiled_sql = self._compiler(
735 dialect,
736 cache_key=elem_cache_key,
737 column_keys=column_keys,
738 for_executemany=for_executemany,
739 schema_translate_map=schema_translate_map,
740 **kw,
741 )
742 # ensure that params of the current statement are not
743 # left in the cache
744 assert not compiled_sql._collect_params # type: ignore[attr-defined] # noqa: E501
745 compiled_cache[key] = compiled_sql
746 else:
747 cache_hit = dialect.CACHE_HIT
748 else:
749 param_dict = None
750 extracted_params = None
751 compiled_sql = self._compiler(
752 dialect,
753 cache_key=None,
754 column_keys=column_keys,
755 for_executemany=for_executemany,
756 schema_translate_map=schema_translate_map,
757 **kw,
758 )
759 # here instead the params need to be extracted, since we don't
760 # have them otherwise
761 assert compiled_sql._collect_params # type: ignore[attr-defined] # noqa: E501
762
763 if not dialect._supports_statement_cache:
764 cache_hit = dialect.NO_DIALECT_SUPPORT
765 elif compiled_cache is None:
766 cache_hit = dialect.CACHING_DISABLED
767 else:
768 cache_hit = dialect.NO_CACHE_KEY
769
770 return compiled_sql, extracted_params, param_dict, cache_hit
771
772 def __invert__(self):
773 # undocumented element currently used by the ORM for
774 # relationship.contains()
775 if hasattr(self, "negation_clause"):
776 return self.negation_clause
777 else:
778 return self._negate()
779
780 def _negate(self) -> ClauseElement:
781 # TODO: this code is uncovered and in all likelihood is not included
782 # in any codepath. So this should raise NotImplementedError in 2.1
783 grouped = self.self_group(against=operators.inv)
784 assert isinstance(grouped, ColumnElement)
785 return UnaryExpression(grouped, operator=operators.inv)
786
787 def __bool__(self):
788 raise TypeError("Boolean value of this clause is not defined")
789
790 def __repr__(self):
791 friendly = self.description
792 if friendly is None:
793 return object.__repr__(self)
794 else:
795 return "<%s.%s at 0x%x; %s>" % (
796 self.__module__,
797 self.__class__.__name__,
798 id(self),
799 friendly,
800 )
801
802
803class DQLDMLClauseElement(ClauseElement):
804 """represents a :class:`.ClauseElement` that compiles to a DQL or DML
805 expression, not DDL.
806
807 .. versionadded:: 2.0
808
809 """
810
811 if typing.TYPE_CHECKING:
812
813 def _compiler(self, dialect: Dialect, **kw: Any) -> SQLCompiler:
814 """Return a compiler appropriate for this ClauseElement, given a
815 Dialect."""
816 ...
817
818 def compile( # noqa: A001
819 self,
820 bind: Optional[_HasDialect] = None,
821 dialect: Optional[Dialect] = None,
822 **kw: Any,
823 ) -> SQLCompiler: ...
824
825
826class CompilerColumnElement(
827 roles.DMLColumnRole,
828 roles.DDLConstraintColumnRole,
829 roles.ColumnsClauseRole,
830 CompilerElement,
831):
832 """A compiler-only column element used for ad-hoc string compilations.
833
834 .. versionadded:: 2.0
835
836 """
837
838 __slots__ = ()
839
840 _propagate_attrs = util.EMPTY_DICT
841 _is_collection_aggregate = False
842 _is_implicitly_boolean = False
843
844 def _with_binary_element_type(self, type_):
845 raise NotImplementedError()
846
847 def _gen_cache_key(self, anon_map, bindparams):
848 raise NotImplementedError()
849
850 @property
851 def _from_objects(self) -> List[FromClause]:
852 raise NotImplementedError()
853
854
855# SQLCoreOperations should be suiting the ExpressionElementRole
856# and ColumnsClauseRole. however the MRO issues become too elaborate
857# at the moment.
858class SQLCoreOperations(Generic[_T_co], ColumnOperators, TypingOnly):
859 __slots__ = ()
860
861 # annotations for comparison methods
862 # these are from operators->Operators / ColumnOperators,
863 # redefined with the specific types returned by ColumnElement hierarchies
864 if typing.TYPE_CHECKING:
865
866 @util.non_memoized_property
867 def _propagate_attrs(self) -> _PropagateAttrsType: ...
868
869 def operate(
870 self, op: OperatorType, *other: Any, **kwargs: Any
871 ) -> ColumnElement[Any]: ...
872
873 def reverse_operate(
874 self, op: OperatorType, other: Any, **kwargs: Any
875 ) -> ColumnElement[Any]: ...
876
877 @overload
878 def op(
879 self,
880 opstring: str,
881 precedence: int = ...,
882 is_comparison: bool = ...,
883 *,
884 return_type: _TypeEngineArgument[_OPT],
885 python_impl: Optional[Callable[..., Any]] = None,
886 operator_class: OperatorClass = ...,
887 visit_name: Optional[str] = ...,
888 ) -> Callable[[Any], BinaryExpression[_OPT]]: ...
889
890 @overload
891 def op(
892 self,
893 opstring: str,
894 precedence: int = ...,
895 is_comparison: bool = ...,
896 return_type: Optional[_TypeEngineArgument[Any]] = ...,
897 python_impl: Optional[Callable[..., Any]] = ...,
898 operator_class: OperatorClass = ...,
899 visit_name: Optional[str] = ...,
900 ) -> Callable[[Any], BinaryExpression[Any]]: ...
901
902 def op(
903 self,
904 opstring: str,
905 precedence: int = 0,
906 is_comparison: bool = False,
907 return_type: Optional[_TypeEngineArgument[Any]] = None,
908 python_impl: Optional[Callable[..., Any]] = None,
909 operator_class: OperatorClass = OperatorClass.BASE,
910 visit_name: Optional[str] = None,
911 ) -> Callable[[Any], BinaryExpression[Any]]: ...
912
913 def bool_op(
914 self,
915 opstring: str,
916 precedence: int = 0,
917 python_impl: Optional[Callable[..., Any]] = None,
918 ) -> Callable[[Any], BinaryExpression[bool]]: ...
919
920 def __and__(self, other: Any) -> BooleanClauseList: ...
921
922 def __or__(self, other: Any) -> BooleanClauseList: ...
923
924 def __invert__(self) -> ColumnElement[_T_co]: ...
925
926 def __lt__(self, other: Any) -> ColumnElement[bool]: ...
927
928 def __le__(self, other: Any) -> ColumnElement[bool]: ...
929
930 # declare also that this class has an hash method otherwise
931 # it may be assumed to be None by type checkers since the
932 # object defines __eq__ and python sets it to None in that case:
933 # https://docs.python.org/3/reference/datamodel.html#object.__hash__
934 def __hash__(self) -> int: ...
935
936 def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
937 ...
938
939 def __ne__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
940 ...
941
942 def is_distinct_from(self, other: Any) -> ColumnElement[bool]: ...
943
944 def is_not_distinct_from(self, other: Any) -> ColumnElement[bool]: ...
945
946 def __gt__(self, other: Any) -> ColumnElement[bool]: ...
947
948 def __ge__(self, other: Any) -> ColumnElement[bool]: ...
949
950 def __neg__(self) -> UnaryExpression[_T_co]: ...
951
952 def __contains__(self, other: Any) -> ColumnElement[bool]: ...
953
954 def __getitem__(self, index: Any) -> ColumnElement[Any]: ...
955
956 @overload
957 def __lshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ...
958
959 @overload
960 def __lshift__(self, other: Any) -> ColumnElement[Any]: ...
961
962 def __lshift__(self, other: Any) -> ColumnElement[Any]: ...
963
964 @overload
965 def __rlshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ...
966
967 @overload
968 def __rlshift__(self, other: Any) -> ColumnElement[Any]: ...
969
970 def __rlshift__(self, other: Any) -> ColumnElement[Any]: ...
971
972 @overload
973 def __rshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ...
974
975 @overload
976 def __rshift__(self, other: Any) -> ColumnElement[Any]: ...
977
978 def __rshift__(self, other: Any) -> ColumnElement[Any]: ...
979
980 @overload
981 def __rrshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ...
982
983 @overload
984 def __rrshift__(self, other: Any) -> ColumnElement[Any]: ...
985
986 def __rrshift__(self, other: Any) -> ColumnElement[Any]: ...
987
988 def __matmul__(self, other: Any) -> ColumnElement[Any]: ...
989
990 def __rmatmul__(self, other: Any) -> ColumnElement[Any]: ...
991
992 @overload
993 def concat(self: _SQO[str], other: Any) -> ColumnElement[str]: ...
994
995 @overload
996 def concat(self, other: Any) -> ColumnElement[Any]: ...
997
998 def concat(self, other: Any) -> ColumnElement[Any]: ...
999
1000 def like(
1001 self, other: Any, escape: Optional[str] = None
1002 ) -> BinaryExpression[bool]: ...
1003
1004 def ilike(
1005 self, other: Any, escape: Optional[str] = None
1006 ) -> BinaryExpression[bool]: ...
1007
1008 def bitwise_xor(self, other: Any) -> BinaryExpression[Any]: ...
1009
1010 def bitwise_or(self, other: Any) -> BinaryExpression[Any]: ...
1011
1012 def bitwise_and(self, other: Any) -> BinaryExpression[Any]: ...
1013
1014 def bitwise_not(self) -> UnaryExpression[_T_co]: ...
1015
1016 def bitwise_lshift(self, other: Any) -> BinaryExpression[Any]: ...
1017
1018 def bitwise_rshift(self, other: Any) -> BinaryExpression[Any]: ...
1019
1020 def in_(
1021 self,
1022 other: Union[
1023 Iterable[Any], BindParameter[Any], roles.InElementRole
1024 ],
1025 ) -> BinaryExpression[bool]: ...
1026
1027 def not_in(
1028 self,
1029 other: Union[
1030 Iterable[Any], BindParameter[Any], roles.InElementRole
1031 ],
1032 ) -> BinaryExpression[bool]: ...
1033
1034 def notin_(
1035 self,
1036 other: Union[
1037 Iterable[Any], BindParameter[Any], roles.InElementRole
1038 ],
1039 ) -> BinaryExpression[bool]: ...
1040
1041 def not_like(
1042 self, other: Any, escape: Optional[str] = None
1043 ) -> BinaryExpression[bool]: ...
1044
1045 def notlike(
1046 self, other: Any, escape: Optional[str] = None
1047 ) -> BinaryExpression[bool]: ...
1048
1049 def not_ilike(
1050 self, other: Any, escape: Optional[str] = None
1051 ) -> BinaryExpression[bool]: ...
1052
1053 def notilike(
1054 self, other: Any, escape: Optional[str] = None
1055 ) -> BinaryExpression[bool]: ...
1056
1057 def is_(self, other: Any) -> BinaryExpression[bool]: ...
1058
1059 def is_not(self, other: Any) -> BinaryExpression[bool]: ...
1060
1061 def isnot(self, other: Any) -> BinaryExpression[bool]: ...
1062
1063 def startswith(
1064 self,
1065 other: Any,
1066 escape: Optional[str] = None,
1067 autoescape: bool = False,
1068 ) -> ColumnElement[bool]: ...
1069
1070 def istartswith(
1071 self,
1072 other: Any,
1073 escape: Optional[str] = None,
1074 autoescape: bool = False,
1075 ) -> ColumnElement[bool]: ...
1076
1077 def endswith(
1078 self,
1079 other: Any,
1080 escape: Optional[str] = None,
1081 autoescape: bool = False,
1082 ) -> ColumnElement[bool]: ...
1083
1084 def iendswith(
1085 self,
1086 other: Any,
1087 escape: Optional[str] = None,
1088 autoescape: bool = False,
1089 ) -> ColumnElement[bool]: ...
1090
1091 def contains(self, other: Any, **kw: Any) -> ColumnElement[bool]: ...
1092
1093 def icontains(self, other: Any, **kw: Any) -> ColumnElement[bool]: ...
1094
1095 def match(self, other: Any, **kwargs: Any) -> ColumnElement[bool]: ...
1096
1097 def regexp_match(
1098 self, pattern: Any, flags: Optional[str] = None
1099 ) -> ColumnElement[bool]: ...
1100
1101 def regexp_replace(
1102 self, pattern: Any, replacement: Any, flags: Optional[str] = None
1103 ) -> ColumnElement[str]: ...
1104
1105 def desc(self) -> UnaryExpression[_T_co]: ...
1106
1107 def asc(self) -> UnaryExpression[_T_co]: ...
1108
1109 def nulls_first(self) -> UnaryExpression[_T_co]: ...
1110
1111 def nullsfirst(self) -> UnaryExpression[_T_co]: ...
1112
1113 def nulls_last(self) -> UnaryExpression[_T_co]: ...
1114
1115 def nullslast(self) -> UnaryExpression[_T_co]: ...
1116
1117 def collate(self, collation: str) -> CollationClause: ...
1118
1119 def between(
1120 self, cleft: Any, cright: Any, symmetric: bool = False
1121 ) -> BinaryExpression[bool]: ...
1122
1123 def distinct(self: _SQO[_T_co]) -> UnaryExpression[_T_co]: ...
1124
1125 def any_(self) -> CollectionAggregate[Any]: ...
1126
1127 def all_(self) -> CollectionAggregate[Any]: ...
1128
1129 # numeric overloads. These need more tweaking
1130 # in particular they all need to have a variant for Optiona[_T]
1131 # because Optional only applies to the data side, not the expression
1132 # side
1133
1134 @overload
1135 def __add__(
1136 self: _SQO[_NMT],
1137 other: Any,
1138 ) -> ColumnElement[_NMT]: ...
1139
1140 @overload
1141 def __add__(
1142 self: _SQO[str],
1143 other: Any,
1144 ) -> ColumnElement[str]: ...
1145
1146 @overload
1147 def __add__(self, other: Any) -> ColumnElement[Any]: ...
1148
1149 def __add__(self, other: Any) -> ColumnElement[Any]: ...
1150
1151 @overload
1152 def __radd__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ...
1153
1154 @overload
1155 def __radd__(self: _SQO[str], other: Any) -> ColumnElement[str]: ...
1156
1157 def __radd__(self, other: Any) -> ColumnElement[Any]: ...
1158
1159 @overload
1160 def __sub__(
1161 self: _SQO[_NMT],
1162 other: Any,
1163 ) -> ColumnElement[_NMT]: ...
1164
1165 @overload
1166 def __sub__(self, other: Any) -> ColumnElement[Any]: ...
1167
1168 def __sub__(self, other: Any) -> ColumnElement[Any]: ...
1169
1170 @overload
1171 def __rsub__(
1172 self: _SQO[_NMT],
1173 other: Any,
1174 ) -> ColumnElement[_NMT]: ...
1175
1176 @overload
1177 def __rsub__(self, other: Any) -> ColumnElement[Any]: ...
1178
1179 def __rsub__(self, other: Any) -> ColumnElement[Any]: ...
1180
1181 @overload
1182 def __mul__(
1183 self: _SQO[_NMT],
1184 other: Any,
1185 ) -> ColumnElement[_NMT]: ...
1186
1187 @overload
1188 def __mul__(self, other: Any) -> ColumnElement[Any]: ...
1189
1190 def __mul__(self, other: Any) -> ColumnElement[Any]: ...
1191
1192 @overload
1193 def __rmul__(
1194 self: _SQO[_NMT],
1195 other: Any,
1196 ) -> ColumnElement[_NMT]: ...
1197
1198 @overload
1199 def __rmul__(self, other: Any) -> ColumnElement[Any]: ...
1200
1201 def __rmul__(self, other: Any) -> ColumnElement[Any]: ...
1202
1203 @overload
1204 def __mod__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ...
1205
1206 @overload
1207 def __mod__(self, other: Any) -> ColumnElement[Any]: ...
1208
1209 def __mod__(self, other: Any) -> ColumnElement[Any]: ...
1210
1211 @overload
1212 def __rmod__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ...
1213
1214 @overload
1215 def __rmod__(self, other: Any) -> ColumnElement[Any]: ...
1216
1217 def __rmod__(self, other: Any) -> ColumnElement[Any]: ...
1218
1219 @overload
1220 def __truediv__(
1221 self: _SQO[int], other: Any
1222 ) -> ColumnElement[_NUMERIC]: ...
1223
1224 @overload
1225 def __truediv__(self: _SQO[_NT], other: Any) -> ColumnElement[_NT]: ...
1226
1227 @overload
1228 def __truediv__(self, other: Any) -> ColumnElement[Any]: ...
1229
1230 def __truediv__(self, other: Any) -> ColumnElement[Any]: ...
1231
1232 @overload
1233 def __rtruediv__(
1234 self: _SQO[_NMT], other: Any
1235 ) -> ColumnElement[_NUMERIC]: ...
1236
1237 @overload
1238 def __rtruediv__(self, other: Any) -> ColumnElement[Any]: ...
1239
1240 def __rtruediv__(self, other: Any) -> ColumnElement[Any]: ...
1241
1242 @overload
1243 def __floordiv__(
1244 self: _SQO[_NMT], other: Any
1245 ) -> ColumnElement[_NMT]: ...
1246
1247 @overload
1248 def __floordiv__(self, other: Any) -> ColumnElement[Any]: ...
1249
1250 def __floordiv__(self, other: Any) -> ColumnElement[Any]: ...
1251
1252 @overload
1253 def __rfloordiv__(
1254 self: _SQO[_NMT], other: Any
1255 ) -> ColumnElement[_NMT]: ...
1256
1257 @overload
1258 def __rfloordiv__(self, other: Any) -> ColumnElement[Any]: ...
1259
1260 def __rfloordiv__(self, other: Any) -> ColumnElement[Any]: ...
1261
1262
1263class SQLColumnExpression(
1264 SQLCoreOperations[_T_co], roles.ExpressionElementRole[_T_co], TypingOnly
1265):
1266 """A type that may be used to indicate any SQL column element or object
1267 that acts in place of one.
1268
1269 :class:`.SQLColumnExpression` is a base of
1270 :class:`.ColumnElement`, as well as within the bases of ORM elements
1271 such as :class:`.InstrumentedAttribute`, and may be used in :pep:`484`
1272 typing to indicate arguments or return values that should behave
1273 as column expressions.
1274
1275 .. versionadded:: 2.0.0b4
1276
1277
1278 """
1279
1280 __slots__ = ()
1281
1282
1283_SQO = SQLCoreOperations
1284
1285
1286class ColumnElement(
1287 roles.ColumnArgumentOrKeyRole,
1288 roles.StatementOptionRole,
1289 roles.WhereHavingRole,
1290 roles.BinaryElementRole[_T],
1291 roles.OrderByRole,
1292 roles.ColumnsClauseRole,
1293 roles.LimitOffsetRole,
1294 roles.DMLColumnRole,
1295 roles.DDLConstraintColumnRole,
1296 roles.DDLExpressionRole,
1297 SQLColumnExpression[_T],
1298 DQLDMLClauseElement,
1299):
1300 """Represent a column-oriented SQL expression suitable for usage in the
1301 "columns" clause, WHERE clause etc. of a statement.
1302
1303 While the most familiar kind of :class:`_expression.ColumnElement` is the
1304 :class:`_schema.Column` object, :class:`_expression.ColumnElement`
1305 serves as the basis
1306 for any unit that may be present in a SQL expression, including
1307 the expressions themselves, SQL functions, bound parameters,
1308 literal expressions, keywords such as ``NULL``, etc.
1309 :class:`_expression.ColumnElement`
1310 is the ultimate base class for all such elements.
1311
1312 A wide variety of SQLAlchemy Core functions work at the SQL expression
1313 level, and are intended to accept instances of
1314 :class:`_expression.ColumnElement` as
1315 arguments. These functions will typically document that they accept a
1316 "SQL expression" as an argument. What this means in terms of SQLAlchemy
1317 usually refers to an input which is either already in the form of a
1318 :class:`_expression.ColumnElement` object,
1319 or a value which can be **coerced** into
1320 one. The coercion rules followed by most, but not all, SQLAlchemy Core
1321 functions with regards to SQL expressions are as follows:
1322
1323 * a literal Python value, such as a string, integer or floating
1324 point value, boolean, datetime, ``Decimal`` object, or virtually
1325 any other Python object, will be coerced into a "literal bound
1326 value". This generally means that a :func:`.bindparam` will be
1327 produced featuring the given value embedded into the construct; the
1328 resulting :class:`.BindParameter` object is an instance of
1329 :class:`_expression.ColumnElement`.
1330 The Python value will ultimately be sent
1331 to the DBAPI at execution time as a parameterized argument to the
1332 ``execute()`` or ``executemany()`` methods, after SQLAlchemy
1333 type-specific converters (e.g. those provided by any associated
1334 :class:`.TypeEngine` objects) are applied to the value.
1335
1336 * any special object value, typically ORM-level constructs, which
1337 feature an accessor called ``__clause_element__()``. The Core
1338 expression system looks for this method when an object of otherwise
1339 unknown type is passed to a function that is looking to coerce the
1340 argument into a :class:`_expression.ColumnElement` and sometimes a
1341 :class:`_expression.SelectBase` expression.
1342 It is used within the ORM to
1343 convert from ORM-specific objects like mapped classes and
1344 mapped attributes into Core expression objects.
1345
1346 * The Python ``None`` value is typically interpreted as ``NULL``,
1347 which in SQLAlchemy Core produces an instance of :func:`.null`.
1348
1349 A :class:`_expression.ColumnElement` provides the ability to generate new
1350 :class:`_expression.ColumnElement`
1351 objects using Python expressions. This means that Python operators
1352 such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations,
1353 and allow the instantiation of further :class:`_expression.ColumnElement`
1354 instances
1355 which are composed from other, more fundamental
1356 :class:`_expression.ColumnElement`
1357 objects. For example, two :class:`.ColumnClause` objects can be added
1358 together with the addition operator ``+`` to produce
1359 a :class:`.BinaryExpression`.
1360 Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses
1361 of :class:`_expression.ColumnElement`:
1362
1363 .. sourcecode:: pycon+sql
1364
1365 >>> from sqlalchemy.sql import column
1366 >>> column("a") + column("b")
1367 <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
1368 >>> print(column("a") + column("b"))
1369 {printsql}a + b
1370
1371 .. seealso::
1372
1373 :class:`_schema.Column`
1374
1375 :func:`_expression.column`
1376
1377 """
1378
1379 __visit_name__ = "column_element"
1380
1381 primary_key: bool = False
1382 _is_clone_of: Optional[ColumnElement[_T]]
1383 _is_column_element = True
1384 _insert_sentinel: bool = False
1385 _omit_from_statements = False
1386 _is_collection_aggregate = False
1387
1388 foreign_keys: AbstractSet[ForeignKey] = frozenset()
1389
1390 @util.memoized_property
1391 def _proxies(self) -> List[ColumnElement[Any]]:
1392 return []
1393
1394 @util.non_memoized_property
1395 def _tq_label(self) -> Optional[str]:
1396 """The named label that can be used to target
1397 this column in a result set in a "table qualified" context.
1398
1399 This label is almost always the label used when
1400 rendering <expr> AS <label> in a SELECT statement when using
1401 the LABEL_STYLE_TABLENAME_PLUS_COL label style, which is what the
1402 legacy ORM ``Query`` object uses as well.
1403
1404 For a regular Column bound to a Table, this is typically the label
1405 <tablename>_<columnname>. For other constructs, different rules
1406 may apply, such as anonymized labels and others.
1407
1408 .. versionchanged:: 1.4.21 renamed from ``._label``
1409
1410 """
1411 return None
1412
1413 key: Optional[str] = None
1414 """The 'key' that in some circumstances refers to this object in a
1415 Python namespace.
1416
1417 This typically refers to the "key" of the column as present in the
1418 ``.c`` collection of a selectable, e.g. ``sometable.c["somekey"]`` would
1419 return a :class:`_schema.Column` with a ``.key`` of "somekey".
1420
1421 """
1422
1423 @HasMemoized.memoized_attribute
1424 def _tq_key_label(self) -> Optional[str]:
1425 """A label-based version of 'key' that in some circumstances refers
1426 to this object in a Python namespace.
1427
1428
1429 _tq_key_label comes into play when a select() statement is constructed
1430 with apply_labels(); in this case, all Column objects in the ``.c``
1431 collection are rendered as <tablename>_<columnname> in SQL; this is
1432 essentially the value of ._label. But to locate those columns in the
1433 ``.c`` collection, the name is along the lines of <tablename>_<key>;
1434 that's the typical value of .key_label.
1435
1436 .. versionchanged:: 1.4.21 renamed from ``._key_label``
1437
1438 """
1439 return self._proxy_key
1440
1441 @property
1442 def _key_label(self) -> Optional[str]:
1443 """legacy; renamed to _tq_key_label"""
1444 return self._tq_key_label
1445
1446 @property
1447 def _label(self) -> Optional[str]:
1448 """legacy; renamed to _tq_label"""
1449 return self._tq_label
1450
1451 @property
1452 def _non_anon_label(self) -> Optional[str]:
1453 """the 'name' that naturally applies this element when rendered in
1454 SQL.
1455
1456 Concretely, this is the "name" of a column or a label in a
1457 SELECT statement; ``<columnname>`` and ``<labelname>`` below:
1458
1459 .. sourcecode:: sql
1460
1461 SELECT <columnmame> FROM table
1462
1463 SELECT column AS <labelname> FROM table
1464
1465 Above, the two names noted will be what's present in the DBAPI
1466 ``cursor.description`` as the names.
1467
1468 If this attribute returns ``None``, it means that the SQL element as
1469 written does not have a 100% fully predictable "name" that would appear
1470 in the ``cursor.description``. Examples include SQL functions, CAST
1471 functions, etc. While such things do return names in
1472 ``cursor.description``, they are only predictable on a
1473 database-specific basis; e.g. an expression like ``MAX(table.col)`` may
1474 appear as the string ``max`` on one database (like PostgreSQL) or may
1475 appear as the whole expression ``max(table.col)`` on SQLite.
1476
1477 The default implementation looks for a ``.name`` attribute on the
1478 object, as has been the precedent established in SQLAlchemy for many
1479 years. An exception is made on the ``FunctionElement`` subclass
1480 so that the return value is always ``None``.
1481
1482 .. versionadded:: 1.4.21
1483
1484
1485
1486 """
1487 return getattr(self, "name", None)
1488
1489 _render_label_in_columns_clause = True
1490 """A flag used by select._columns_plus_names that helps to determine
1491 we are actually going to render in terms of "SELECT <col> AS <label>".
1492 This flag can be returned as False for some Column objects that want
1493 to be rendered as simple "SELECT <col>"; typically columns that don't have
1494 any parent table and are named the same as what the label would be
1495 in any case.
1496
1497 """
1498
1499 _allow_label_resolve = True
1500 """A flag that can be flipped to prevent a column from being resolvable
1501 by string label name.
1502
1503 The joined eager loader strategy in the ORM uses this, for example.
1504
1505 """
1506
1507 _is_implicitly_boolean = False
1508
1509 _alt_names: Sequence[str] = ()
1510
1511 if TYPE_CHECKING:
1512
1513 def _ungroup(self) -> ColumnElement[_T]: ...
1514
1515 @overload
1516 def self_group(self, against: None = None) -> ColumnElement[_T]: ...
1517
1518 @overload
1519 def self_group(
1520 self, against: Optional[OperatorType] = None
1521 ) -> ColumnElement[Any]: ...
1522
1523 def self_group(
1524 self, against: Optional[OperatorType] = None
1525 ) -> ColumnElement[Any]:
1526 if (
1527 against in (operators.and_, operators.or_, operators._asbool)
1528 and self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity
1529 ):
1530 return AsBoolean(self, operators.is_true, operators.is_false)
1531 elif against in (operators.any_op, operators.all_op):
1532 return Grouping(self)
1533 else:
1534 return self
1535
1536 @overload
1537 def _negate(self: ColumnElement[bool]) -> ColumnElement[bool]: ...
1538
1539 @overload
1540 def _negate(self: ColumnElement[_T]) -> ColumnElement[_T]: ...
1541
1542 def _negate(self) -> ColumnElement[Any]:
1543 if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity:
1544 return AsBoolean(self, operators.is_false, operators.is_true)
1545 else:
1546 grouped = self.self_group(against=operators.inv)
1547 assert isinstance(grouped, ColumnElement)
1548 return UnaryExpression(
1549 grouped,
1550 operator=operators.inv,
1551 )
1552
1553 type: TypeEngine[_T]
1554
1555 if not TYPE_CHECKING:
1556
1557 @util.memoized_property
1558 def type(self) -> TypeEngine[_T]: # noqa: A001
1559 # used for delayed setup of
1560 # type_api
1561 return type_api.NULLTYPE
1562
1563 @HasMemoized.memoized_attribute
1564 def comparator(self) -> TypeEngine.Comparator[_T]:
1565 try:
1566 comparator_factory = self.type.comparator_factory
1567 except AttributeError as err:
1568 raise TypeError(
1569 "Object %r associated with '.type' attribute "
1570 "is not a TypeEngine class or object" % self.type
1571 ) from err
1572 else:
1573 return comparator_factory(self)
1574
1575 def __setstate__(self, state):
1576 self.__dict__.update(state)
1577
1578 def __getattr__(self, key: str) -> Any:
1579 try:
1580 return getattr(self.comparator, key)
1581 except AttributeError as err:
1582 raise AttributeError(
1583 "Neither %r object nor %r object has an attribute %r"
1584 % (
1585 type(self).__name__,
1586 type(self.comparator).__name__,
1587 key,
1588 )
1589 ) from err
1590
1591 def operate(
1592 self,
1593 op: operators.OperatorType,
1594 *other: Any,
1595 **kwargs: Any,
1596 ) -> ColumnElement[Any]:
1597 return op(self.comparator, *other, **kwargs) # type: ignore[no-any-return] # noqa: E501
1598
1599 def reverse_operate(
1600 self, op: operators.OperatorType, other: Any, **kwargs: Any
1601 ) -> ColumnElement[Any]:
1602 return op(other, self.comparator, **kwargs) # type: ignore[no-any-return] # noqa: E501
1603
1604 def _bind_param(
1605 self,
1606 operator: operators.OperatorType,
1607 obj: Any,
1608 type_: Optional[TypeEngine[_T]] = None,
1609 expanding: bool = False,
1610 ) -> BindParameter[_T]:
1611 return BindParameter(
1612 None,
1613 obj,
1614 _compared_to_operator=operator,
1615 type_=type_,
1616 _compared_to_type=self.type,
1617 unique=True,
1618 expanding=expanding,
1619 )
1620
1621 @property
1622 def expression(self) -> ColumnElement[Any]:
1623 """Return a column expression.
1624
1625 Part of the inspection interface; returns self.
1626
1627 """
1628 return self
1629
1630 @property
1631 def _select_iterable(self) -> _SelectIterable:
1632 return (self,)
1633
1634 @util.memoized_property
1635 def base_columns(self) -> FrozenSet[ColumnElement[Any]]:
1636 return frozenset(c for c in self.proxy_set if not c._proxies)
1637
1638 @util.memoized_property
1639 def proxy_set(self) -> FrozenSet[ColumnElement[Any]]:
1640 """set of all columns we are proxying
1641
1642 as of 2.0 this is explicitly deannotated columns. previously it was
1643 effectively deannotated columns but wasn't enforced. annotated
1644 columns should basically not go into sets if at all possible because
1645 their hashing behavior is very non-performant.
1646
1647 """
1648 return frozenset([self._deannotate()]).union(
1649 itertools.chain(*[c.proxy_set for c in self._proxies])
1650 )
1651
1652 @util.memoized_property
1653 def _expanded_proxy_set(self) -> FrozenSet[ColumnElement[Any]]:
1654 return frozenset(_expand_cloned(self.proxy_set))
1655
1656 def _uncached_proxy_list(self) -> List[ColumnElement[Any]]:
1657 """An 'uncached' version of proxy set.
1658
1659 This list includes annotated columns which perform very poorly in
1660 set operations.
1661
1662 """
1663
1664 return [self] + list(
1665 itertools.chain(*[c._uncached_proxy_list() for c in self._proxies])
1666 )
1667
1668 def shares_lineage(self, othercolumn: ColumnElement[Any]) -> bool:
1669 """Return True if the given :class:`_expression.ColumnElement`
1670 has a common ancestor to this :class:`_expression.ColumnElement`."""
1671
1672 return bool(self.proxy_set.intersection(othercolumn.proxy_set))
1673
1674 def _compare_name_for_result(self, other: ColumnElement[Any]) -> bool:
1675 """Return True if the given column element compares to this one
1676 when targeting within a result row."""
1677
1678 return (
1679 hasattr(other, "name")
1680 and hasattr(self, "name")
1681 and other.name == self.name
1682 )
1683
1684 @HasMemoized.memoized_attribute
1685 def _proxy_key(self) -> Optional[str]:
1686 if self._annotations and "proxy_key" in self._annotations:
1687 return cast(str, self._annotations["proxy_key"])
1688
1689 name = self.key
1690 if not name:
1691 # there's a bit of a seeming contradiction which is that the
1692 # "_non_anon_label" of a column can in fact be an
1693 # "_anonymous_label"; this is when it's on a column that is
1694 # proxying for an anonymous expression in a subquery.
1695 name = self._non_anon_label
1696
1697 if isinstance(name, _anonymous_label):
1698 return None
1699 else:
1700 return name
1701
1702 @HasMemoized.memoized_attribute
1703 def _expression_label(self) -> Optional[str]:
1704 """a suggested label to use in the case that the column has no name,
1705 which should be used if possible as the explicit 'AS <label>'
1706 where this expression would normally have an anon label.
1707
1708 this is essentially mostly what _proxy_key does except it returns
1709 None if the column has a normal name that can be used.
1710
1711 """
1712
1713 if getattr(self, "name", None) is not None:
1714 return None
1715 elif self._annotations and "proxy_key" in self._annotations:
1716 return cast(str, self._annotations["proxy_key"])
1717 else:
1718 return None
1719
1720 def _make_proxy(
1721 self,
1722 selectable: FromClause,
1723 *,
1724 primary_key: ColumnSet,
1725 foreign_keys: Set[KeyedColumnElement[Any]],
1726 name: Optional[str] = None,
1727 key: Optional[str] = None,
1728 name_is_truncatable: bool = False,
1729 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
1730 **kw: Any,
1731 ) -> typing_Tuple[str, ColumnClause[_T]]:
1732 """Create a new :class:`_expression.ColumnElement` representing this
1733 :class:`_expression.ColumnElement` as it appears in the select list of
1734 a descending selectable.
1735
1736 """
1737 if name is None:
1738 name = self._anon_name_label
1739 if key is None:
1740 key = self._proxy_key
1741 else:
1742 key = name
1743
1744 assert key is not None
1745
1746 co: ColumnClause[_T] = ColumnClause(
1747 (
1748 coercions.expect(roles.TruncatedLabelRole, name)
1749 if name_is_truncatable
1750 else name
1751 ),
1752 type_=getattr(self, "type", None),
1753 _selectable=selectable,
1754 )
1755
1756 co._propagate_attrs = selectable._propagate_attrs
1757 if compound_select_cols:
1758 co._proxies = list(compound_select_cols)
1759 else:
1760 co._proxies = [self]
1761 if selectable._is_clone_of is not None:
1762 co._is_clone_of = selectable._is_clone_of.columns.get(key)
1763 return key, co
1764
1765 def cast(self, type_: _TypeEngineArgument[_OPT]) -> Cast[_OPT]:
1766 """Produce a type cast, i.e. ``CAST(<expression> AS <type>)``.
1767
1768 This is a shortcut to the :func:`_expression.cast` function.
1769
1770 .. seealso::
1771
1772 :ref:`tutorial_casts`
1773
1774 :func:`_expression.cast`
1775
1776 :func:`_expression.type_coerce`
1777
1778 """
1779 return Cast(self, type_)
1780
1781 def label(self, name: Optional[str]) -> Label[_T]:
1782 """Produce a column label, i.e. ``<columnname> AS <name>``.
1783
1784 This is a shortcut to the :func:`_expression.label` function.
1785
1786 If 'name' is ``None``, an anonymous label name will be generated.
1787
1788 """
1789 return Label(name, self, self.type)
1790
1791 def _anon_label(
1792 self, seed: Optional[str], add_hash: Optional[int] = None
1793 ) -> _anonymous_label:
1794 while self._is_clone_of is not None:
1795 self = self._is_clone_of
1796
1797 # as of 1.4 anonymous label for ColumnElement uses hash(), not id(),
1798 # as the identifier, because a column and its annotated version are
1799 # the same thing in a SQL statement
1800 hash_value = hash(self)
1801
1802 if add_hash:
1803 # this path is used for disambiguating anon labels that would
1804 # otherwise be the same name for the same element repeated.
1805 # an additional numeric value is factored in for each label.
1806
1807 # shift hash(self) (which is id(self), typically 8 byte integer)
1808 # 16 bits leftward. fill extra add_hash on right
1809 assert add_hash < (2 << 15)
1810 assert seed
1811 hash_value = (hash_value << 16) | add_hash
1812
1813 # extra underscore is added for labels with extra hash
1814 # values, to isolate the "deduped anon" namespace from the
1815 # regular namespace. eliminates chance of these
1816 # manufactured hash values overlapping with regular ones for some
1817 # undefined python interpreter
1818 seed = seed + "_"
1819
1820 if isinstance(seed, _anonymous_label):
1821 # NOTE: the space after the hash is required
1822 return _anonymous_label(f"{seed}%({hash_value} )s")
1823
1824 return _anonymous_label.safe_construct(hash_value, seed or "anon")
1825
1826 @util.memoized_property
1827 def _anon_name_label(self) -> str:
1828 """Provides a constant 'anonymous label' for this ColumnElement.
1829
1830 This is a label() expression which will be named at compile time.
1831 The same label() is returned each time ``anon_label`` is called so
1832 that expressions can reference ``anon_label`` multiple times,
1833 producing the same label name at compile time.
1834
1835 The compiler uses this function automatically at compile time
1836 for expressions that are known to be 'unnamed' like binary
1837 expressions and function calls.
1838
1839 .. versionchanged:: 1.4.9 - this attribute was not intended to be
1840 public and is renamed to _anon_name_label. anon_name exists
1841 for backwards compat
1842
1843 """
1844 name = getattr(self, "name", None)
1845 return self._anon_label(name)
1846
1847 @util.memoized_property
1848 def _anon_key_label(self) -> _anonymous_label:
1849 """Provides a constant 'anonymous key label' for this ColumnElement.
1850
1851 Compare to ``anon_label``, except that the "key" of the column,
1852 if available, is used to generate the label.
1853
1854 This is used when a deduplicating key is placed into the columns
1855 collection of a selectable.
1856
1857 .. versionchanged:: 1.4.9 - this attribute was not intended to be
1858 public and is renamed to _anon_key_label. anon_key_label exists
1859 for backwards compat
1860
1861 """
1862 return self._anon_label(self._proxy_key)
1863
1864 @property
1865 @util.deprecated(
1866 "1.4",
1867 "The :attr:`_expression.ColumnElement.anon_label` attribute is now "
1868 "private, and the public accessor is deprecated.",
1869 )
1870 def anon_label(self) -> str:
1871 return self._anon_name_label
1872
1873 @property
1874 @util.deprecated(
1875 "1.4",
1876 "The :attr:`_expression.ColumnElement.anon_key_label` attribute is "
1877 "now private, and the public accessor is deprecated.",
1878 )
1879 def anon_key_label(self) -> str:
1880 return self._anon_key_label
1881
1882 def _dedupe_anon_label_idx(self, idx: int) -> str:
1883 """label to apply to a column that is anon labeled, but repeated
1884 in the SELECT, so that we have to make an "extra anon" label that
1885 disambiguates it from the previous appearance.
1886
1887 these labels come out like "foo_bar_id__1" and have double underscores
1888 in them.
1889
1890 """
1891 label = getattr(self, "name", None)
1892
1893 # current convention is that if the element doesn't have a
1894 # ".name" (usually because it is not NamedColumn), we try to
1895 # use a "table qualified" form for the "dedupe anon" label,
1896 # based on the notion that a label like
1897 # "CAST(casttest.v1 AS DECIMAL) AS casttest_v1__1" looks better than
1898 # "CAST(casttest.v1 AS DECIMAL) AS anon__1"
1899
1900 if label is None:
1901 return self._dedupe_anon_tq_label_idx(idx)
1902 else:
1903 return self._anon_label(label, add_hash=idx)
1904
1905 @util.memoized_property
1906 def _anon_tq_label(self) -> _anonymous_label:
1907 return self._anon_label(getattr(self, "_tq_label", None))
1908
1909 @util.memoized_property
1910 def _anon_tq_key_label(self) -> _anonymous_label:
1911 return self._anon_label(getattr(self, "_tq_key_label", None))
1912
1913 def _dedupe_anon_tq_label_idx(self, idx: int) -> _anonymous_label:
1914 label = getattr(self, "_tq_label", None) or "anon"
1915
1916 return self._anon_label(label, add_hash=idx)
1917
1918
1919class KeyedColumnElement(ColumnElement[_T]):
1920 """ColumnElement where ``.key`` is non-None."""
1921
1922 _is_keyed_column_element = True
1923
1924 key: str
1925
1926
1927class WrapsColumnExpression(ColumnElement[_T]):
1928 """Mixin that defines a :class:`_expression.ColumnElement`
1929 as a wrapper with special
1930 labeling behavior for an expression that already has a name.
1931
1932 .. versionadded:: 1.4
1933
1934 .. seealso::
1935
1936 :ref:`change_4449`
1937
1938
1939 """
1940
1941 @property
1942 def wrapped_column_expression(self) -> ColumnElement[_T]:
1943 raise NotImplementedError()
1944
1945 @util.non_memoized_property
1946 def _tq_label(self) -> Optional[str]:
1947 wce = self.wrapped_column_expression
1948 if hasattr(wce, "_tq_label"):
1949 return wce._tq_label
1950 else:
1951 return None
1952
1953 @property
1954 def _label(self) -> Optional[str]:
1955 return self._tq_label
1956
1957 @property
1958 def _non_anon_label(self) -> Optional[str]:
1959 return None
1960
1961 @util.non_memoized_property
1962 def _anon_name_label(self) -> str:
1963 wce = self.wrapped_column_expression
1964
1965 # this logic tries to get the WrappedColumnExpression to render
1966 # with "<expr> AS <name>", where "<name>" is the natural name
1967 # within the expression itself. e.g. "CAST(table.foo) AS foo".
1968 if not wce._is_text_clause:
1969 nal = wce._non_anon_label
1970 if nal:
1971 return nal
1972 elif hasattr(wce, "_anon_name_label"):
1973 return wce._anon_name_label
1974 return super()._anon_name_label
1975
1976 def _dedupe_anon_label_idx(self, idx: int) -> str:
1977 wce = self.wrapped_column_expression
1978 nal = wce._non_anon_label
1979 if nal:
1980 return self._anon_label(nal + "_")
1981 else:
1982 return self._dedupe_anon_tq_label_idx(idx)
1983
1984 @property
1985 def _proxy_key(self):
1986 wce = self.wrapped_column_expression
1987
1988 if not wce._is_text_clause:
1989 return wce._proxy_key
1990 return super()._proxy_key
1991
1992
1993class DMLTargetCopy(roles.InElementRole, KeyedColumnElement[_T]):
1994 """Refer to another column's VALUES or SET expression in an INSERT or
1995 UPDATE statement.
1996
1997 See the public-facing :func:`_sql.from_dml_column` constructor for
1998 background.
1999
2000 .. versionadded:: 2.1
2001
2002
2003 """
2004
2005 def __init__(self, column: _OnlyColumnArgument[_T]):
2006 self.column = coercions.expect(roles.ColumnArgumentRole, column)
2007 self.type = self.column.type
2008
2009 __visit_name__ = "dmltargetcopy"
2010
2011 _traverse_internals: _TraverseInternalsType = [
2012 ("column", InternalTraversal.dp_clauseelement),
2013 ]
2014
2015
2016class BindParameter(roles.InElementRole, KeyedColumnElement[_T]):
2017 r"""Represent a "bound expression".
2018
2019 :class:`.BindParameter` is invoked explicitly using the
2020 :func:`.bindparam` function, as in::
2021
2022 from sqlalchemy import bindparam
2023
2024 stmt = select(users_table).where(
2025 users_table.c.name == bindparam("username")
2026 )
2027
2028 Detailed discussion of how :class:`.BindParameter` is used is
2029 at :func:`.bindparam`.
2030
2031 .. seealso::
2032
2033 :func:`.bindparam`
2034
2035 """
2036
2037 __visit_name__ = "bindparam"
2038
2039 _traverse_internals: _TraverseInternalsType = [
2040 ("key", InternalTraversal.dp_anon_name),
2041 ("type", InternalTraversal.dp_type),
2042 ("callable", InternalTraversal.dp_plain_dict),
2043 ("value", InternalTraversal.dp_plain_obj),
2044 ("literal_execute", InternalTraversal.dp_boolean),
2045 ]
2046
2047 key: str
2048 _anon_map_key: Optional[str] = None
2049 type: TypeEngine[_T]
2050 value: Optional[_T]
2051
2052 _is_crud = False
2053 _is_bind_parameter = True
2054
2055 # bindparam implements its own _gen_cache_key() method however
2056 # we check subclasses for this flag, else no cache key is generated
2057 inherit_cache = True
2058
2059 def __init__(
2060 self,
2061 key: Optional[str],
2062 value: Any = _NoArg.NO_ARG,
2063 type_: Optional[_TypeEngineArgument[_T]] = None,
2064 unique: bool = False,
2065 required: Union[bool, Literal[_NoArg.NO_ARG]] = _NoArg.NO_ARG,
2066 quote: Optional[bool] = None,
2067 callable_: Optional[Callable[[], Any]] = None,
2068 expanding: bool = False,
2069 isoutparam: bool = False,
2070 literal_execute: bool = False,
2071 _compared_to_operator: Optional[OperatorType] = None,
2072 _compared_to_type: Optional[TypeEngine[Any]] = None,
2073 _is_crud: bool = False,
2074 ):
2075 if required is _NoArg.NO_ARG:
2076 required = value is _NoArg.NO_ARG and callable_ is None
2077 if value is _NoArg.NO_ARG:
2078 value = None
2079
2080 if quote is not None:
2081 key = quoted_name.construct(key, quote)
2082
2083 if unique:
2084 self.key, self._anon_map_key = (
2085 _anonymous_label.safe_construct_with_key(
2086 id(self),
2087 (
2088 key
2089 if key is not None
2090 and not isinstance(key, _anonymous_label)
2091 else "param"
2092 ),
2093 sanitize_key=True,
2094 )
2095 )
2096 elif key:
2097 self.key = key
2098 else:
2099 self.key, self._anon_map_key = (
2100 _anonymous_label.safe_construct_with_key(id(self), "param")
2101 )
2102
2103 # identifying key that won't change across
2104 # clones, used to identify the bind's logical
2105 # identity
2106 self._identifying_key = self.key
2107
2108 # key that was passed in the first place, used to
2109 # generate new keys
2110 self._orig_key = key or "param"
2111
2112 self.unique = unique
2113 self.value = value
2114 self.callable = callable_
2115 self.isoutparam = isoutparam
2116 self.required = required
2117
2118 # indicate an "expanding" parameter; the compiler sets this
2119 # automatically in the compiler _render_in_expr_w_bindparam method
2120 # for an IN expression
2121 self.expanding = expanding
2122
2123 # this is another hint to help w/ expanding and is typically
2124 # set in the compiler _render_in_expr_w_bindparam method for an
2125 # IN expression
2126 self.expand_op = None
2127
2128 self.literal_execute = literal_execute
2129 if _is_crud:
2130 self._is_crud = True
2131
2132 if type_ is None:
2133 if expanding:
2134 if value:
2135 check_value = value[0]
2136 else:
2137 check_value = type_api._NO_VALUE_IN_LIST
2138 else:
2139 check_value = value
2140 if _compared_to_type is not None:
2141 self.type = _compared_to_type.coerce_compared_value(
2142 _compared_to_operator, check_value
2143 )
2144 else:
2145 self.type = type_api._resolve_value_to_type(check_value)
2146 elif isinstance(type_, type):
2147 self.type = type_()
2148 elif is_tuple_type(type_):
2149 if value:
2150 if expanding:
2151 check_value = value[0]
2152 else:
2153 check_value = value
2154 cast("BindParameter[TupleAny]", self).type = (
2155 type_._resolve_values_to_types(check_value)
2156 )
2157 else:
2158 cast("BindParameter[TupleAny]", self).type = type_
2159 else:
2160 self.type = type_
2161
2162 def _with_value(self, value, maintain_key=False, required=NO_ARG):
2163 """Return a copy of this :class:`.BindParameter` with the given value
2164 set.
2165 """
2166 cloned = self._clone(maintain_key=maintain_key)
2167 cloned.value = value
2168 cloned.callable = None
2169 cloned.required = required if required is not NO_ARG else self.required
2170 if cloned.type is type_api.NULLTYPE:
2171 cloned.type = type_api._resolve_value_to_type(value)
2172 return cloned
2173
2174 @property
2175 def effective_value(self) -> Optional[_T]:
2176 """Return the value of this bound parameter,
2177 taking into account if the ``callable`` parameter
2178 was set.
2179
2180 The ``callable`` value will be evaluated
2181 and returned if present, else ``value``.
2182
2183 """
2184 if self.callable:
2185 # TODO: set up protocol for bind parameter callable
2186 return self.callable() # type: ignore
2187 else:
2188 return self.value
2189
2190 def render_literal_execute(self) -> Self:
2191 """Produce a copy of this bound parameter that will enable the
2192 :paramref:`_sql.BindParameter.literal_execute` flag.
2193
2194 The :paramref:`_sql.BindParameter.literal_execute` flag will
2195 have the effect of the parameter rendered in the compiled SQL
2196 string using ``[POSTCOMPILE]`` form, which is a special form that
2197 is converted to be a rendering of the literal value of the parameter
2198 at SQL execution time. The rationale is to support caching
2199 of SQL statement strings that can embed per-statement literal values,
2200 such as LIMIT and OFFSET parameters, in the final SQL string that
2201 is passed to the DBAPI. Dialects in particular may want to use
2202 this method within custom compilation schemes.
2203
2204 .. versionadded:: 1.4.5
2205
2206 .. seealso::
2207
2208 :ref:`engine_thirdparty_caching`
2209
2210 """
2211 c: Self = ClauseElement._clone(self)
2212 c.literal_execute = True
2213 return c
2214
2215 def _negate_in_binary(self, negated_op, original_op):
2216 if self.expand_op is original_op:
2217 bind = self._clone()
2218 bind.expand_op = negated_op
2219 return bind
2220 else:
2221 return self
2222
2223 def _with_binary_element_type(self, type_: TypeEngine[Any]) -> Self:
2224 c: Self = ClauseElement._clone(self)
2225 c.type = type_
2226 return c
2227
2228 def _clone(self, maintain_key: bool = False, **kw: Any) -> Self:
2229 c: Self = ClauseElement._clone(self, **kw)
2230 # ensure all the BindParameter objects stay in cloned set.
2231 # in #7823, we changed "clone" so that a clone only keeps a reference
2232 # to the "original" element, since for column correspondence, that's
2233 # all we need. However, for BindParam, _cloned_set is used by
2234 # the "cache key bind match" lookup, which means if any of those
2235 # interim BindParameter objects became part of a cache key in the
2236 # cache, we need it. So here, make sure all clones keep carrying
2237 # forward.
2238 c._cloned_set.update(self._cloned_set)
2239 if not maintain_key and self.unique:
2240 c.key, c._anon_map_key = _anonymous_label.safe_construct_with_key(
2241 id(c), c._orig_key or "param", sanitize_key=True
2242 )
2243 return c
2244
2245 def _gen_cache_key(self, anon_map, bindparams):
2246 _gen_cache_ok = self.__class__.__dict__.get("inherit_cache", False)
2247
2248 if not _gen_cache_ok:
2249 if anon_map is not None:
2250 anon_map[NO_CACHE] = True
2251 return None
2252
2253 id_, found = anon_map.get_anon(self)
2254 if found:
2255 return (id_, self.__class__)
2256
2257 if bindparams is not None:
2258 bindparams.append(self)
2259
2260 return (
2261 id_,
2262 self.__class__,
2263 self.type._static_cache_key,
2264 (
2265 anon_map[self._anon_map_key]
2266 if self._anon_map_key is not None
2267 else self.key
2268 ),
2269 self.literal_execute,
2270 )
2271
2272 def _convert_to_unique(self):
2273 if not self.unique:
2274 self.unique = True
2275 self.key, self._anon_map_key = (
2276 _anonymous_label.safe_construct_with_key(
2277 id(self), self._orig_key or "param", sanitize_key=True
2278 )
2279 )
2280
2281 def __getstate__(self):
2282 """execute a deferred value for serialization purposes."""
2283
2284 d = self.__dict__.copy()
2285 v = self.value
2286 if self.callable:
2287 v = self.callable()
2288 d["callable"] = None
2289 d["value"] = v
2290 return d
2291
2292 def __setstate__(self, state):
2293 if state.get("unique", False):
2294 anon_and_key = _anonymous_label.safe_construct_with_key(
2295 id(self), state.get("_orig_key", "param"), sanitize_key=True
2296 )
2297 state["key"], state["_anon_map_key"] = anon_and_key
2298 self.__dict__.update(state)
2299
2300 def __repr__(self):
2301 return "%s(%r, %r, type_=%r)" % (
2302 self.__class__.__name__,
2303 self.key,
2304 self.value,
2305 self.type,
2306 )
2307
2308
2309class TypeClause(DQLDMLClauseElement):
2310 """Handle a type keyword in a SQL statement.
2311
2312 Used by the ``Case`` statement.
2313
2314 """
2315
2316 __visit_name__ = "typeclause"
2317
2318 _traverse_internals: _TraverseInternalsType = [
2319 ("type", InternalTraversal.dp_type)
2320 ]
2321 type: TypeEngine[Any]
2322
2323 def __init__(self, type_: TypeEngine[Any]):
2324 self.type = type_
2325
2326
2327class AbstractTextClause(
2328 roles.DDLConstraintColumnRole,
2329 roles.DDLExpressionRole,
2330 roles.StatementOptionRole,
2331 roles.WhereHavingRole,
2332 roles.OrderByRole,
2333 roles.FromClauseRole,
2334 roles.SelectStatementRole,
2335 roles.InElementRole,
2336 Generative,
2337 ExecutableStatement,
2338 DQLDMLClauseElement,
2339 roles.BinaryElementRole[Any],
2340):
2341 """Base class for textual SQL constructs like TextClause and TString."""
2342
2343 __visit_name__: str
2344
2345 _is_text_clause = True
2346 _is_textual = True
2347 _is_implicitly_boolean = False
2348 _render_label_in_columns_clause = False
2349 _omit_from_statements = False
2350 _is_collection_aggregate = False
2351
2352 @property
2353 def _hide_froms(self) -> Iterable[FromClause]:
2354 return ()
2355
2356 def __and__(self, other):
2357 # support use in select.where(), query.filter()
2358 return and_(self, other)
2359
2360 @property
2361 def _select_iterable(self) -> _SelectIterable:
2362 return (self,)
2363
2364 # help in those cases where text/tstring() is
2365 # interpreted in a column expression situation
2366 key: Optional[str] = None
2367 _label: Optional[str] = None
2368
2369 _allow_label_resolve = False
2370
2371 @property
2372 def type(self) -> TypeEngine[Any]:
2373 return type_api.NULLTYPE
2374
2375 @property
2376 def comparator(self):
2377 return self.type.comparator_factory(self) # type: ignore
2378
2379 def self_group(
2380 self, against: Optional[OperatorType] = None
2381 ) -> Union[Self, Grouping[Any]]:
2382 if against is operators.in_op:
2383 return Grouping(self)
2384 else:
2385 return self
2386
2387 def bindparams(
2388 self,
2389 *binds: BindParameter[Any],
2390 **names_to_values: Any,
2391 ) -> Self:
2392 """Establish the values and/or types of bound parameters within
2393 this :class:`_expression.AbstractTextClause` construct.
2394
2395 This is implemented only for :class:`.TextClause` will raise
2396 ``NotImplementedError`` for :class:`.TString`.
2397
2398 """
2399 raise NotImplementedError()
2400
2401 @util.preload_module("sqlalchemy.sql.selectable")
2402 def columns(
2403 self,
2404 *cols: _OnlyColumnArgument[Any],
2405 **types: _TypeEngineArgument[Any],
2406 ) -> TextualSelect:
2407 r"""Turn this :class:`_expression.AbstractTextClause` object into a
2408 :class:`_expression.TextualSelect`
2409 object that serves the same role as a SELECT
2410 statement.
2411
2412 The :class:`_expression.TextualSelect` is part of the
2413 :class:`_expression.SelectBase`
2414 hierarchy and can be embedded into another statement by using the
2415 :meth:`_expression.TextualSelect.subquery` method to produce a
2416 :class:`.Subquery`
2417 object, which can then be SELECTed from.
2418
2419 This function essentially bridges the gap between an entirely
2420 textual SELECT statement and the SQL expression language concept
2421 of a "selectable"::
2422
2423 from sqlalchemy.sql import column, text
2424
2425 stmt = text("SELECT id, name FROM some_table")
2426 stmt = stmt.columns(column("id"), column("name")).subquery("st")
2427
2428 stmt = (
2429 select(mytable)
2430 .select_from(mytable.join(stmt, mytable.c.name == stmt.c.name))
2431 .where(stmt.c.id > 5)
2432 )
2433
2434 Above, we pass a series of :func:`_expression.column` elements to the
2435 :meth:`_expression.AbstractTextClause.columns` method positionally.
2436 These :func:`_expression.column` elements now become first class
2437 elements upon the :attr:`_expression.TextualSelect.selected_columns`
2438 column collection, which then become part of the :attr:`.Subquery.c`
2439 collection after :meth:`_expression.TextualSelect.subquery` is invoked.
2440
2441 The column expressions we pass to
2442 :meth:`_expression.AbstractTextClause.columns` may also be typed; when
2443 we do so, these :class:`.TypeEngine` objects become the effective
2444 return type of the column, so that SQLAlchemy's result-set-processing
2445 systems may be used on the return values. This is often needed for
2446 types such as date or boolean types, as well as for unicode processing
2447 on some dialect configurations::
2448
2449 stmt = text("SELECT id, name, timestamp FROM some_table")
2450 stmt = stmt.columns(
2451 column("id", Integer),
2452 column("name", Unicode),
2453 column("timestamp", DateTime),
2454 )
2455
2456 for id, name, timestamp in connection.execute(stmt):
2457 print(id, name, timestamp)
2458
2459 As a shortcut to the above syntax, keyword arguments referring to
2460 types alone may be used, if only type conversion is needed::
2461
2462 stmt = text("SELECT id, name, timestamp FROM some_table")
2463 stmt = stmt.columns(id=Integer, name=Unicode, timestamp=DateTime)
2464
2465 for id, name, timestamp in connection.execute(stmt):
2466 print(id, name, timestamp)
2467
2468 The positional form of :meth:`_expression.AbstractTextClause.columns`
2469 also provides the unique feature of **positional column targeting**,
2470 which is particularly useful when using the ORM with complex textual
2471 queries. If we specify the columns from our model to
2472 :meth:`_expression.AbstractTextClause.columns`, the result set will
2473 match to those columns positionally, meaning the name or origin of the
2474 column in the textual SQL doesn't matter::
2475
2476 stmt = text(
2477 "SELECT users.id, addresses.id, users.id, "
2478 "users.name, addresses.email_address AS email "
2479 "FROM users JOIN addresses ON users.id=addresses.user_id "
2480 "WHERE users.id = 1"
2481 ).columns(
2482 User.id,
2483 Address.id,
2484 Address.user_id,
2485 User.name,
2486 Address.email_address,
2487 )
2488
2489 query = (
2490 session.query(User)
2491 .from_statement(stmt)
2492 .options(contains_eager(User.addresses))
2493 )
2494
2495 The :meth:`_expression.AbstractTextClause.columns` method provides a
2496 direct route to calling :meth:`_expression.FromClause.subquery` as well
2497 as :meth:`_expression.SelectBase.cte` against a textual SELECT
2498 statement::
2499
2500 stmt = stmt.columns(id=Integer, name=String).cte("st")
2501
2502 stmt = select(sometable).where(sometable.c.id == stmt.c.id)
2503
2504 :param \*cols: A series of :class:`_expression.ColumnElement` objects,
2505 typically
2506 :class:`_schema.Column` objects from a :class:`_schema.Table`
2507 or ORM level
2508 column-mapped attributes, representing a set of columns that this
2509 textual string will SELECT from.
2510
2511 :param \**types: A mapping of string names to :class:`.TypeEngine`
2512 type objects indicating the datatypes to use for names that are
2513 SELECTed from the textual string. Prefer to use the ``*cols``
2514 argument as it also indicates positional ordering.
2515
2516 """
2517 selectable = util.preloaded.sql_selectable
2518
2519 input_cols: List[NamedColumn[Any]] = [
2520 coercions.expect(roles.LabeledColumnExprRole, col) for col in cols
2521 ]
2522
2523 positional_input_cols = [
2524 (
2525 ColumnClause(col.key, types.pop(col.key))
2526 if col.key in types
2527 else col
2528 )
2529 for col in input_cols
2530 ]
2531 keyed_input_cols: List[NamedColumn[Any]] = [
2532 ColumnClause(key, type_) for key, type_ in types.items()
2533 ]
2534
2535 elem = selectable.TextualSelect.__new__(selectable.TextualSelect)
2536 elem._init(
2537 self,
2538 positional_input_cols + keyed_input_cols,
2539 positional=bool(positional_input_cols) and not keyed_input_cols,
2540 )
2541 return elem
2542
2543
2544class TextClause(AbstractTextClause, inspection.Inspectable["TextClause"]):
2545 """Represent a literal SQL text fragment.
2546
2547 E.g.::
2548
2549 from sqlalchemy import text
2550
2551 t = text("SELECT * FROM users")
2552 result = connection.execute(t)
2553
2554 The :class:`_expression.TextClause` construct is produced using the
2555 :func:`_expression.text`
2556 function; see that function for full documentation.
2557
2558 .. seealso::
2559
2560 :func:`_expression.text`
2561
2562 """
2563
2564 __visit_name__ = "textclause"
2565
2566 _traverse_internals: _TraverseInternalsType = [
2567 ("_bindparams", InternalTraversal.dp_string_clauseelement_dict),
2568 ("text", InternalTraversal.dp_string),
2569 ] + ExecutableStatement._executable_traverse_internals
2570
2571 _bind_params_regex = re.compile(r"(?<![:\w\x5c]):(\w+)(?!:)", re.UNICODE)
2572
2573 @property
2574 def _is_star(self) -> bool: # type: ignore[override]
2575 return self.text == "*"
2576
2577 def __init__(self, text: str):
2578 self._bindparams: Dict[str, BindParameter[Any]] = {}
2579
2580 def repl(m):
2581 self._bindparams[m.group(1)] = BindParameter(m.group(1))
2582 return ":%s" % m.group(1)
2583
2584 # scan the string and search for bind parameter names, add them
2585 # to the list of bindparams
2586 self.text = self._bind_params_regex.sub(repl, text)
2587
2588 @_generative
2589 def bindparams(
2590 self,
2591 *binds: BindParameter[Any],
2592 **names_to_values: Any,
2593 ) -> Self:
2594 """Establish the values and/or types of bound parameters within
2595 this :class:`_expression.TextClause` construct.
2596
2597 Given a text construct such as::
2598
2599 from sqlalchemy import text
2600
2601 stmt = text(
2602 "SELECT id, name FROM user WHERE name=:name AND timestamp=:timestamp"
2603 )
2604
2605 the :meth:`_expression.TextClause.bindparams`
2606 method can be used to establish
2607 the initial value of ``:name`` and ``:timestamp``,
2608 using simple keyword arguments::
2609
2610 stmt = stmt.bindparams(
2611 name="jack", timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)
2612 )
2613
2614 Where above, new :class:`.BindParameter` objects
2615 will be generated with the names ``name`` and ``timestamp``, and
2616 values of ``jack`` and ``datetime.datetime(2012, 10, 8, 15, 12, 5)``,
2617 respectively. The types will be
2618 inferred from the values given, in this case :class:`.String` and
2619 :class:`.DateTime`.
2620
2621 When specific typing behavior is needed, the positional ``*binds``
2622 argument can be used in which to specify :func:`.bindparam` constructs
2623 directly. These constructs must include at least the ``key``
2624 argument, then an optional value and type::
2625
2626 from sqlalchemy import bindparam
2627
2628 stmt = stmt.bindparams(
2629 bindparam("name", value="jack", type_=String),
2630 bindparam("timestamp", type_=DateTime),
2631 )
2632
2633 Above, we specified the type of :class:`.DateTime` for the
2634 ``timestamp`` bind, and the type of :class:`.String` for the ``name``
2635 bind. In the case of ``name`` we also set the default value of
2636 ``"jack"``.
2637
2638 Additional bound parameters can be supplied at statement execution
2639 time, e.g.::
2640
2641 result = connection.execute(
2642 stmt, timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)
2643 )
2644
2645 The :meth:`_expression.TextClause.bindparams`
2646 method can be called repeatedly,
2647 where it will re-use existing :class:`.BindParameter` objects to add
2648 new information. For example, we can call
2649 :meth:`_expression.TextClause.bindparams`
2650 first with typing information, and a
2651 second time with value information, and it will be combined::
2652
2653 stmt = text(
2654 "SELECT id, name FROM user WHERE name=:name "
2655 "AND timestamp=:timestamp"
2656 )
2657 stmt = stmt.bindparams(
2658 bindparam("name", type_=String), bindparam("timestamp", type_=DateTime)
2659 )
2660 stmt = stmt.bindparams(
2661 name="jack", timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)
2662 )
2663
2664 The :meth:`_expression.TextClause.bindparams`
2665 method also supports the concept of
2666 **unique** bound parameters. These are parameters that are
2667 "uniquified" on name at statement compilation time, so that multiple
2668 :func:`_expression.text`
2669 constructs may be combined together without the names
2670 conflicting. To use this feature, specify the
2671 :paramref:`.BindParameter.unique` flag on each :func:`.bindparam`
2672 object::
2673
2674 stmt1 = text("select id from table where name=:name").bindparams(
2675 bindparam("name", value="name1", unique=True)
2676 )
2677 stmt2 = text("select id from table where name=:name").bindparams(
2678 bindparam("name", value="name2", unique=True)
2679 )
2680
2681 union = union_all(stmt1.columns(column("id")), stmt2.columns(column("id")))
2682
2683 The above statement will render as:
2684
2685 .. sourcecode:: sql
2686
2687 select id from table where name=:name_1
2688 UNION ALL select id from table where name=:name_2
2689
2690 """ # noqa: E501
2691 self._bindparams = new_params = self._bindparams.copy()
2692
2693 for bind in binds:
2694 try:
2695 # the regex used for text() currently will not match
2696 # a unique/anonymous key in any case, so use the _orig_key
2697 # so that a text() construct can support unique parameters
2698 existing = new_params[bind._orig_key]
2699 except KeyError as err:
2700 raise exc.ArgumentError(
2701 "This text() construct doesn't define a "
2702 "bound parameter named %r" % bind._orig_key
2703 ) from err
2704 else:
2705 new_params[existing._orig_key] = bind
2706
2707 for key, value in names_to_values.items():
2708 try:
2709 existing = new_params[key]
2710 except KeyError as err:
2711 raise exc.ArgumentError(
2712 "This text() construct doesn't define a "
2713 "bound parameter named %r" % key
2714 ) from err
2715 else:
2716 new_params[key] = existing._with_value(value, required=False)
2717 return self
2718
2719 @property
2720 def type(self) -> TypeEngine[Any]:
2721 return type_api.NULLTYPE
2722
2723 @property
2724 def comparator(self):
2725 # TODO: this seems wrong, it seems like we might not
2726 # be using this method.
2727 return self.type.comparator_factory(self) # type: ignore
2728
2729 def self_group(
2730 self, against: Optional[OperatorType] = None
2731 ) -> Union[Self, Grouping[Any]]:
2732 if against is operators.in_op:
2733 return Grouping(self)
2734 else:
2735 return self
2736
2737
2738class TString(AbstractTextClause, inspection.Inspectable["TString"]):
2739 """Represent a SQL template string using Python 3.14+ t-strings.
2740
2741 E.g.::
2742
2743 from sqlalchemy import tstring, column
2744
2745 a = 5
2746 b = 10
2747 stmt = tstring(t"select {a}, {b}")
2748 result = connection.execute(stmt)
2749
2750 The :class:`_expression.TString` construct is produced using the
2751 :func:`_expression.tstring` function; see that function for full
2752 documentation.
2753
2754 .. versionadded:: 2.1
2755
2756 .. seealso::
2757
2758 :func:`_expression.tstring`
2759
2760 """
2761
2762 __visit_name__ = "tstring"
2763
2764 _traverse_internals: _TraverseInternalsType = [
2765 ("parts", InternalTraversal.dp_clauseelement_list)
2766 ] + ExecutableStatement._executable_traverse_internals
2767
2768 @property
2769 def _is_star(self) -> bool: # type: ignore[override]
2770 return (
2771 len(self.parts) == 1
2772 and isinstance(self.parts[0], TextClause)
2773 and self.parts[0]._is_star
2774 )
2775
2776 def __init__(self, template: Template):
2777 """Construct a :class:`_expression.TString` from a Python 3.14+
2778 template string.
2779
2780 :param template: a Python 3.14+ template string (t-string) that
2781 contains SQL fragments and Python expressions to be interpolated.
2782
2783 """
2784 self.parts: List[ClauseElement] = []
2785
2786 if not isinstance(template, Template):
2787 raise exc.ArgumentError("pep-750 Tstring (e.g. t'...') expected")
2788
2789 for part in template:
2790 if isinstance(part, str):
2791 self.parts.append(TextClause(part))
2792 else:
2793 assert hasattr(part, "value")
2794 self.parts.append(
2795 coercions.expect(roles.TStringElementRole, part.value)
2796 )
2797
2798 def bindparams(
2799 self,
2800 *binds: BindParameter[Any],
2801 **names_to_values: Any,
2802 ) -> Self:
2803 """Not supported for TString constructs.
2804
2805 TString constructs do not support .bindparams(). Bind parameters
2806 are automatically created from interpolated values.
2807
2808 """
2809 raise NotImplementedError(
2810 "TString constructs do not support .bindparams(). "
2811 "Bind parameters are automatically created "
2812 "from interpolated values."
2813 )
2814
2815
2816class Null(SingletonConstant, roles.ConstExprRole[None], ColumnElement[None]):
2817 """Represent the NULL keyword in a SQL statement.
2818
2819 :class:`.Null` is accessed as a constant via the
2820 :func:`.null` function.
2821
2822 """
2823
2824 __visit_name__ = "null"
2825
2826 _traverse_internals: _TraverseInternalsType = []
2827 _singleton: Null
2828
2829 if not TYPE_CHECKING:
2830
2831 @util.memoized_property
2832 def type(self) -> TypeEngine[_T]: # noqa: A001
2833 return type_api.NULLTYPE
2834
2835 @classmethod
2836 def _instance(cls) -> Null:
2837 """Return a constant :class:`.Null` construct."""
2838
2839 return Null._singleton
2840
2841
2842Null._create_singleton()
2843
2844
2845class False_(
2846 SingletonConstant, roles.ConstExprRole[bool], ColumnElement[bool]
2847):
2848 """Represent the ``false`` keyword, or equivalent, in a SQL statement.
2849
2850 :class:`.False_` is accessed as a constant via the
2851 :func:`.false` function.
2852
2853 """
2854
2855 __visit_name__ = "false"
2856 _traverse_internals: _TraverseInternalsType = []
2857 _singleton: False_
2858
2859 if not TYPE_CHECKING:
2860
2861 @util.memoized_property
2862 def type(self) -> TypeEngine[_T]: # noqa: A001
2863 return type_api.BOOLEANTYPE
2864
2865 def _negate(self) -> True_:
2866 return True_._singleton
2867
2868 @classmethod
2869 def _instance(cls) -> False_:
2870 return False_._singleton
2871
2872
2873False_._create_singleton()
2874
2875
2876class True_(SingletonConstant, roles.ConstExprRole[bool], ColumnElement[bool]):
2877 """Represent the ``true`` keyword, or equivalent, in a SQL statement.
2878
2879 :class:`.True_` is accessed as a constant via the
2880 :func:`.true` function.
2881
2882 """
2883
2884 __visit_name__ = "true"
2885
2886 _traverse_internals: _TraverseInternalsType = []
2887 _singleton: True_
2888
2889 if not TYPE_CHECKING:
2890
2891 @util.memoized_property
2892 def type(self) -> TypeEngine[_T]: # noqa: A001
2893 return type_api.BOOLEANTYPE
2894
2895 def _negate(self) -> False_:
2896 return False_._singleton
2897
2898 @classmethod
2899 def _ifnone(
2900 cls, other: Optional[ColumnElement[Any]]
2901 ) -> ColumnElement[Any]:
2902 if other is None:
2903 return cls._instance()
2904 else:
2905 return other
2906
2907 @classmethod
2908 def _instance(cls) -> True_:
2909 return True_._singleton
2910
2911
2912True_._create_singleton()
2913
2914
2915class ElementList(DQLDMLClauseElement):
2916 """Describe a list of clauses that will be space separated.
2917
2918 This is a minimal version of :class:`.ClauseList` which is used by
2919 the :class:`.HasSyntaxExtension` class. It does not do any coercions
2920 so should be used internally only.
2921
2922 .. versionadded:: 2.1
2923
2924 """
2925
2926 __visit_name__ = "element_list"
2927
2928 _traverse_internals: _TraverseInternalsType = [
2929 ("clauses", InternalTraversal.dp_clauseelement_tuple),
2930 ]
2931
2932 clauses: typing_Tuple[ClauseElement, ...]
2933
2934 def __init__(self, clauses: Sequence[ClauseElement]):
2935 self.clauses = tuple(clauses)
2936
2937
2938class OrderByList(
2939 roles.OrderByRole,
2940 operators.OrderingOperators,
2941 DQLDMLClauseElement,
2942):
2943 """Describe a list of clauses that will be comma separated to nest
2944 within an ORDER BY.
2945
2946 .. versionadded:: 2.1
2947
2948 """
2949
2950 __visit_name__ = "order_by_list"
2951
2952 _traverse_internals: _TraverseInternalsType = [
2953 ("clauses", InternalTraversal.dp_clauseelement_tuple),
2954 ]
2955
2956 clauses: List[ColumnElement[Any]]
2957
2958 def __init__(
2959 self,
2960 clauses: Iterable[Union[OrderByList, _ColumnExpressionArgument[Any]]],
2961 ):
2962 text_converter_role: Type[roles.SQLRole] = roles.ByOfRole
2963 self._text_converter_role = text_converter_role
2964
2965 self.clauses = [
2966 coercions.expect(
2967 text_converter_role, clause, apply_propagate_attrs=self
2968 )
2969 for clause in clauses
2970 ]
2971
2972 def __iter__(self) -> Iterator[ColumnElement[Any]]:
2973 return iter(self.clauses)
2974
2975 def __len__(self) -> int:
2976 return len(self.clauses)
2977
2978 @property
2979 def _select_iterable(self) -> _SelectIterable:
2980 return itertools.chain.from_iterable(
2981 [elem._select_iterable for elem in self.clauses]
2982 )
2983
2984 @util.ro_non_memoized_property
2985 def _from_objects(self) -> List[FromClause]:
2986 return list(itertools.chain(*[c._from_objects for c in self.clauses]))
2987
2988 def self_group(
2989 self, against: Optional[OperatorType] = None
2990 ) -> Union[Self, Grouping[Any]]:
2991 return self
2992
2993 def desc(self) -> OrderByList:
2994 return OrderByList([e.desc() for e in self.clauses])
2995
2996 def asc(self) -> OrderByList:
2997 return OrderByList([e.asc() for e in self.clauses])
2998
2999 def nulls_first(self) -> OrderByList:
3000 return OrderByList([e.nulls_first() for e in self.clauses])
3001
3002 def nulls_last(self) -> OrderByList:
3003 return OrderByList([e.nulls_last() for e in self.clauses])
3004
3005
3006class ClauseList(
3007 roles.InElementRole,
3008 roles.OrderByRole,
3009 roles.ColumnsClauseRole,
3010 roles.DMLColumnRole,
3011 DQLDMLClauseElement,
3012):
3013 """Describe a list of clauses, separated by an operator.
3014
3015 By default, is comma-separated, such as a column listing.
3016
3017 """
3018
3019 __visit_name__ = "clauselist"
3020
3021 # Used by ORM context.py to identify ClauseList objects in legacy
3022 # composite attribute queries (see test_query_cols_legacy test)
3023 _is_clause_list = True
3024
3025 _traverse_internals: _TraverseInternalsType = [
3026 ("clauses", InternalTraversal.dp_clauseelement_list),
3027 ("operator", InternalTraversal.dp_operator),
3028 ]
3029
3030 clauses: List[ColumnElement[Any]]
3031
3032 def __init__(
3033 self,
3034 *clauses: _ColumnExpressionArgument[Any],
3035 operator: OperatorType = operators.comma_op,
3036 group: bool = True,
3037 group_contents: bool = True,
3038 _literal_as_text_role: Type[roles.SQLRole] = roles.WhereHavingRole,
3039 ):
3040 self.operator = operator
3041 self.group = group
3042 self.group_contents = group_contents
3043 clauses_iterator: Iterable[_ColumnExpressionArgument[Any]] = clauses
3044 text_converter_role: Type[roles.SQLRole] = _literal_as_text_role
3045 self._text_converter_role = text_converter_role
3046
3047 if self.group_contents:
3048 self.clauses = [
3049 coercions.expect(
3050 text_converter_role, clause, apply_propagate_attrs=self
3051 ).self_group(against=self.operator)
3052 for clause in clauses_iterator
3053 ]
3054 else:
3055 self.clauses = [
3056 coercions.expect(
3057 text_converter_role, clause, apply_propagate_attrs=self
3058 )
3059 for clause in clauses_iterator
3060 ]
3061 self._is_implicitly_boolean = operators.is_boolean(self.operator)
3062
3063 @classmethod
3064 def _construct_raw(
3065 cls,
3066 operator: OperatorType,
3067 clauses: Optional[Sequence[ColumnElement[Any]]] = None,
3068 ) -> ClauseList:
3069 self = cls.__new__(cls)
3070 self.clauses = list(clauses) if clauses else []
3071 self.group = True
3072 self.operator = operator
3073 self.group_contents = True
3074 self._is_implicitly_boolean = False
3075 return self
3076
3077 def __iter__(self) -> Iterator[ColumnElement[Any]]:
3078 return iter(self.clauses)
3079
3080 def __len__(self) -> int:
3081 return len(self.clauses)
3082
3083 @property
3084 def _select_iterable(self) -> _SelectIterable:
3085 return itertools.chain.from_iterable(
3086 [elem._select_iterable for elem in self.clauses]
3087 )
3088
3089 def append(self, clause):
3090 if self.group_contents:
3091 self.clauses.append(
3092 coercions.expect(self._text_converter_role, clause).self_group(
3093 against=self.operator
3094 )
3095 )
3096 else:
3097 self.clauses.append(
3098 coercions.expect(self._text_converter_role, clause)
3099 )
3100
3101 @util.ro_non_memoized_property
3102 def _from_objects(self) -> List[FromClause]:
3103 return list(itertools.chain(*[c._from_objects for c in self.clauses]))
3104
3105 def self_group(
3106 self, against: Optional[OperatorType] = None
3107 ) -> Union[Self, Grouping[Any]]:
3108 if self.group and operators.is_precedent(self.operator, against):
3109 return Grouping(self)
3110 else:
3111 return self
3112
3113
3114class OperatorExpression(ColumnElement[_T]):
3115 """base for expressions that contain an operator and operands
3116
3117 .. versionadded:: 2.0
3118
3119 """
3120
3121 operator: OperatorType
3122 type: TypeEngine[_T]
3123
3124 group: bool = True
3125
3126 @property
3127 def is_comparison(self):
3128 return operators.is_comparison(self.operator)
3129
3130 def self_group(
3131 self, against: Optional[OperatorType] = None
3132 ) -> Union[Self, Grouping[_T]]:
3133 if (
3134 self.group
3135 and operators.is_precedent(self.operator, against)
3136 or (
3137 # a negate against a non-boolean operator
3138 # doesn't make too much sense but we should
3139 # group for that
3140 against is operators.inv
3141 and not operators.is_boolean(self.operator)
3142 )
3143 ):
3144 return Grouping(self)
3145 else:
3146 return self
3147
3148 @property
3149 def _flattened_operator_clauses(
3150 self,
3151 ) -> typing_Tuple[ColumnElement[Any], ...]:
3152 raise NotImplementedError()
3153
3154 @classmethod
3155 def _construct_for_op(
3156 cls,
3157 left: ColumnElement[Any],
3158 right: ColumnElement[Any],
3159 op: OperatorType,
3160 *,
3161 type_: TypeEngine[_T],
3162 negate: Optional[OperatorType] = None,
3163 modifiers: Optional[Mapping[str, Any]] = None,
3164 ) -> OperatorExpression[_T]:
3165 if operators.is_associative(op):
3166 assert (
3167 negate is None
3168 ), f"negate not supported for associative operator {op}"
3169
3170 multi = False
3171 if getattr(
3172 left, "operator", None
3173 ) is op and type_._compare_type_affinity(left.type):
3174 multi = True
3175 left_flattened = left._flattened_operator_clauses
3176 else:
3177 left_flattened = (left,)
3178
3179 if getattr(
3180 right, "operator", None
3181 ) is op and type_._compare_type_affinity(right.type):
3182 multi = True
3183 right_flattened = right._flattened_operator_clauses
3184 else:
3185 right_flattened = (right,)
3186
3187 if multi:
3188 return ExpressionClauseList._construct_for_list(
3189 op,
3190 type_,
3191 *(left_flattened + right_flattened),
3192 )
3193
3194 if right._is_collection_aggregate:
3195 negate = None
3196
3197 return BinaryExpression(
3198 left, right, op, type_=type_, negate=negate, modifiers=modifiers
3199 )
3200
3201
3202class ExpressionClauseList(OperatorExpression[_T]):
3203 """Describe a list of clauses, separated by an operator,
3204 in a column expression context.
3205
3206 :class:`.ExpressionClauseList` differs from :class:`.ClauseList` in that
3207 it represents a column-oriented DQL expression only, not an open ended
3208 list of anything comma separated.
3209
3210 .. versionadded:: 2.0
3211
3212 """
3213
3214 __visit_name__ = "expression_clauselist"
3215
3216 _traverse_internals: _TraverseInternalsType = [
3217 ("clauses", InternalTraversal.dp_clauseelement_tuple),
3218 ("operator", InternalTraversal.dp_operator),
3219 ]
3220
3221 clauses: typing_Tuple[ColumnElement[Any], ...]
3222
3223 group: bool
3224
3225 def __init__(
3226 self,
3227 operator: OperatorType,
3228 *clauses: _ColumnExpressionArgument[Any],
3229 type_: Optional[_TypeEngineArgument[_T]] = None,
3230 ):
3231 self.operator = operator
3232
3233 self.clauses = tuple(
3234 coercions.expect(
3235 roles.ExpressionElementRole, clause, apply_propagate_attrs=self
3236 )
3237 for clause in clauses
3238 )
3239 self._is_implicitly_boolean = operators.is_boolean(self.operator)
3240 self.type = type_api.to_instance(type_) # type: ignore
3241
3242 @property
3243 def _flattened_operator_clauses(
3244 self,
3245 ) -> typing_Tuple[ColumnElement[Any], ...]:
3246 return self.clauses
3247
3248 def __iter__(self) -> Iterator[ColumnElement[Any]]:
3249 return iter(self.clauses)
3250
3251 def __len__(self) -> int:
3252 return len(self.clauses)
3253
3254 @property
3255 def _select_iterable(self) -> _SelectIterable:
3256 return (self,)
3257
3258 @util.ro_non_memoized_property
3259 def _from_objects(self) -> List[FromClause]:
3260 return list(itertools.chain(*[c._from_objects for c in self.clauses]))
3261
3262 def _append_inplace(self, clause: ColumnElement[Any]) -> None:
3263 self.clauses += (clause,)
3264
3265 @classmethod
3266 def _construct_for_list(
3267 cls,
3268 operator: OperatorType,
3269 type_: TypeEngine[_T],
3270 *clauses: ColumnElement[Any],
3271 group: bool = True,
3272 ) -> ExpressionClauseList[_T]:
3273 self = cls.__new__(cls)
3274 self.group = group
3275 if group:
3276 self.clauses = tuple(
3277 c.self_group(against=operator) for c in clauses
3278 )
3279 else:
3280 self.clauses = clauses
3281 self.operator = operator
3282 self.type = type_
3283 for c in clauses:
3284 if c._propagate_attrs:
3285 self._propagate_attrs = c._propagate_attrs
3286 break
3287 return self
3288
3289 def _negate(self) -> Any:
3290 grouped = self.self_group(against=operators.inv)
3291 assert isinstance(grouped, ColumnElement)
3292 return UnaryExpression(grouped, operator=operators.inv)
3293
3294
3295class BooleanClauseList(ExpressionClauseList[bool]):
3296 __visit_name__ = "expression_clauselist"
3297 inherit_cache = True
3298
3299 def __init__(self, *arg, **kw):
3300 raise NotImplementedError(
3301 "BooleanClauseList has a private constructor"
3302 )
3303
3304 @classmethod
3305 def _process_clauses_for_boolean(
3306 cls,
3307 operator: OperatorType,
3308 continue_on: Any,
3309 skip_on: Any,
3310 clauses: Iterable[ColumnElement[Any]],
3311 ) -> typing_Tuple[int, List[ColumnElement[Any]]]:
3312 has_continue_on = None
3313
3314 convert_clauses = []
3315
3316 against = operators._asbool
3317 lcc = 0
3318
3319 for clause in clauses:
3320 if clause is continue_on:
3321 # instance of continue_on, like and_(x, y, True, z), store it
3322 # if we didn't find one already, we will use it if there
3323 # are no other expressions here.
3324 has_continue_on = clause
3325 elif clause is skip_on:
3326 # instance of skip_on, e.g. and_(x, y, False, z), cancels
3327 # the rest out
3328 convert_clauses = [clause]
3329 lcc = 1
3330 break
3331 else:
3332 if not lcc:
3333 lcc = 1
3334 else:
3335 against = operator
3336 # technically this would be len(convert_clauses) + 1
3337 # however this only needs to indicate "greater than one"
3338 lcc = 2
3339 convert_clauses.append(clause)
3340
3341 if not convert_clauses and has_continue_on is not None:
3342 convert_clauses = [has_continue_on]
3343 lcc = 1
3344
3345 return lcc, [c.self_group(against=against) for c in convert_clauses]
3346
3347 @classmethod
3348 def _construct(
3349 cls,
3350 operator: OperatorType,
3351 continue_on: Any,
3352 skip_on: Any,
3353 initial_clause: Any = _NoArg.NO_ARG,
3354 *clauses: Any,
3355 **kw: Any,
3356 ) -> ColumnElement[Any]:
3357 if initial_clause is _NoArg.NO_ARG:
3358 # no elements period. deprecated use case. return an empty
3359 # ClauseList construct that generates nothing unless it has
3360 # elements added to it.
3361 name = operator.__name__
3362
3363 util.warn_deprecated(
3364 f"Invoking {name}() without arguments is deprecated, and "
3365 f"will be disallowed in a future release. For an empty "
3366 f"""{name}() construct, use '{name}({
3367 'true()' if continue_on is True_._singleton else 'false()'
3368 }, *args)' """
3369 f"""or '{name}({
3370 'True' if continue_on is True_._singleton else 'False'
3371 }, *args)'.""",
3372 version="1.4",
3373 )
3374 return cls._construct_raw(operator)
3375
3376 lcc, convert_clauses = cls._process_clauses_for_boolean(
3377 operator,
3378 continue_on,
3379 skip_on,
3380 [
3381 coercions.expect(roles.WhereHavingRole, clause)
3382 for clause in util.coerce_generator_arg(
3383 (initial_clause,) + clauses
3384 )
3385 ],
3386 )
3387
3388 if lcc > 1:
3389 # multiple elements. Return regular BooleanClauseList
3390 # which will link elements against the operator.
3391
3392 flattened_clauses = itertools.chain.from_iterable(
3393 (
3394 (c for c in to_flat._flattened_operator_clauses)
3395 if getattr(to_flat, "operator", None) is operator
3396 else (to_flat,)
3397 )
3398 for to_flat in convert_clauses
3399 )
3400
3401 return cls._construct_raw(operator, flattened_clauses) # type: ignore # noqa: E501
3402 else:
3403 assert lcc
3404 # just one element. return it as a single boolean element,
3405 # not a list and discard the operator.
3406 return convert_clauses[0]
3407
3408 @classmethod
3409 def _construct_for_whereclause(
3410 cls, clauses: Iterable[ColumnElement[Any]]
3411 ) -> Optional[ColumnElement[bool]]:
3412 operator, continue_on, skip_on = (
3413 operators.and_,
3414 True_._singleton,
3415 False_._singleton,
3416 )
3417
3418 lcc, convert_clauses = cls._process_clauses_for_boolean(
3419 operator,
3420 continue_on,
3421 skip_on,
3422 clauses, # these are assumed to be coerced already
3423 )
3424
3425 if lcc > 1:
3426 # multiple elements. Return regular BooleanClauseList
3427 # which will link elements against the operator.
3428 return cls._construct_raw(operator, convert_clauses)
3429 elif lcc == 1:
3430 # just one element. return it as a single boolean element,
3431 # not a list and discard the operator.
3432 return convert_clauses[0]
3433 else:
3434 return None
3435
3436 @classmethod
3437 def _construct_raw(
3438 cls,
3439 operator: OperatorType,
3440 clauses: Optional[Sequence[ColumnElement[Any]]] = None,
3441 ) -> BooleanClauseList:
3442 self = cls.__new__(cls)
3443 self.clauses = tuple(clauses) if clauses else ()
3444 self.group = True
3445 self.operator = operator
3446 self.type = type_api.BOOLEANTYPE
3447 self._is_implicitly_boolean = True
3448 return self
3449
3450 @classmethod
3451 def and_(
3452 cls,
3453 initial_clause: Union[
3454 Literal[True], _ColumnExpressionArgument[bool], _NoArg
3455 ] = _NoArg.NO_ARG,
3456 *clauses: _ColumnExpressionArgument[bool],
3457 ) -> ColumnElement[bool]:
3458 r"""Produce a conjunction of expressions joined by ``AND``.
3459
3460 See :func:`_sql.and_` for full documentation.
3461 """
3462 return cls._construct(
3463 operators.and_,
3464 True_._singleton,
3465 False_._singleton,
3466 initial_clause,
3467 *clauses,
3468 )
3469
3470 @classmethod
3471 def or_(
3472 cls,
3473 initial_clause: Union[
3474 Literal[False], _ColumnExpressionArgument[bool], _NoArg
3475 ] = _NoArg.NO_ARG,
3476 *clauses: _ColumnExpressionArgument[bool],
3477 ) -> ColumnElement[bool]:
3478 """Produce a conjunction of expressions joined by ``OR``.
3479
3480 See :func:`_sql.or_` for full documentation.
3481 """
3482 return cls._construct(
3483 operators.or_,
3484 False_._singleton,
3485 True_._singleton,
3486 initial_clause,
3487 *clauses,
3488 )
3489
3490 @property
3491 def _select_iterable(self) -> _SelectIterable:
3492 return (self,)
3493
3494 def self_group(
3495 self, against: Optional[OperatorType] = None
3496 ) -> Union[Self, Grouping[bool]]:
3497 if not self.clauses:
3498 return self
3499 else:
3500 return super().self_group(against=against)
3501
3502
3503and_ = BooleanClauseList.and_
3504or_ = BooleanClauseList.or_
3505
3506
3507class Tuple(ClauseList, ColumnElement[TupleAny]):
3508 """Represent a SQL tuple."""
3509
3510 __visit_name__ = "tuple"
3511
3512 _traverse_internals: _TraverseInternalsType = (
3513 ClauseList._traverse_internals + []
3514 )
3515
3516 type: TupleType
3517
3518 @util.preload_module("sqlalchemy.sql.sqltypes")
3519 def __init__(
3520 self,
3521 *clauses: _ColumnExpressionArgument[Any],
3522 types: Optional[Sequence[_TypeEngineArgument[Any]]] = None,
3523 ):
3524 sqltypes = util.preloaded.sql_sqltypes
3525
3526 if types is None:
3527 init_clauses: List[ColumnElement[Any]] = [
3528 coercions.expect(roles.ExpressionElementRole, c)
3529 for c in clauses
3530 ]
3531 else:
3532 if len(types) != len(clauses):
3533 raise exc.ArgumentError(
3534 "Wrong number of elements for %d-tuple: %r "
3535 % (len(types), clauses)
3536 )
3537 init_clauses = [
3538 coercions.expect(
3539 roles.ExpressionElementRole,
3540 c,
3541 type_=typ if not typ._isnull else None,
3542 )
3543 for typ, c in zip(types, clauses)
3544 ]
3545
3546 self.type = sqltypes.TupleType(*[arg.type for arg in init_clauses])
3547 super().__init__(*init_clauses)
3548
3549 @property
3550 def _select_iterable(self) -> _SelectIterable:
3551 return (self,)
3552
3553 def _bind_param(self, operator, obj, type_=None, expanding=False):
3554 if expanding:
3555 return BindParameter(
3556 None,
3557 value=obj,
3558 _compared_to_operator=operator,
3559 unique=True,
3560 expanding=True,
3561 type_=type_,
3562 _compared_to_type=self.type,
3563 )
3564 else:
3565 return Tuple(
3566 *[
3567 BindParameter(
3568 None,
3569 o,
3570 _compared_to_operator=operator,
3571 _compared_to_type=compared_to_type,
3572 unique=True,
3573 type_=type_,
3574 )
3575 for o, compared_to_type in zip(obj, self.type.types)
3576 ]
3577 )
3578
3579 def self_group(self, against: Optional[OperatorType] = None) -> Self:
3580 # Tuple is parenthesized by definition.
3581 return self
3582
3583
3584class Case(ColumnElement[_T]):
3585 """Represent a ``CASE`` expression.
3586
3587 :class:`.Case` is produced using the :func:`.case` factory function,
3588 as in::
3589
3590 from sqlalchemy import case
3591
3592 stmt = select(users_table).where(
3593 case(
3594 (users_table.c.name == "wendy", "W"),
3595 (users_table.c.name == "jack", "J"),
3596 else_="E",
3597 )
3598 )
3599
3600 Details on :class:`.Case` usage is at :func:`.case`.
3601
3602 .. seealso::
3603
3604 :func:`.case`
3605
3606 """
3607
3608 __visit_name__ = "case"
3609
3610 _traverse_internals: _TraverseInternalsType = [
3611 ("value", InternalTraversal.dp_clauseelement),
3612 ("whens", InternalTraversal.dp_clauseelement_tuples),
3613 ("else_", InternalTraversal.dp_clauseelement),
3614 ]
3615
3616 # for case(), the type is derived from the whens. so for the moment
3617 # users would have to cast() the case to get a specific type
3618
3619 whens: List[typing_Tuple[ColumnElement[bool], ColumnElement[_T]]]
3620 else_: Optional[ColumnElement[_T]]
3621 value: Optional[ColumnElement[Any]]
3622
3623 def __init__(
3624 self,
3625 *whens: Union[
3626 typing_Tuple[_ColumnExpressionArgument[bool], Any],
3627 Mapping[Any, Any],
3628 ],
3629 value: Optional[Any] = None,
3630 else_: Optional[Any] = None,
3631 ):
3632 new_whens: Iterable[Any] = coercions._expression_collection_was_a_list(
3633 "whens", "case", whens
3634 )
3635 try:
3636 new_whens = util.dictlike_iteritems(new_whens)
3637 except TypeError:
3638 pass
3639
3640 self.whens = [
3641 (
3642 coercions.expect(
3643 roles.ExpressionElementRole,
3644 c,
3645 apply_propagate_attrs=self,
3646 ).self_group(),
3647 coercions.expect(roles.ExpressionElementRole, r),
3648 )
3649 for (c, r) in new_whens
3650 ]
3651
3652 if value is None:
3653 self.value = None
3654 else:
3655 self.value = coercions.expect(roles.ExpressionElementRole, value)
3656
3657 if else_ is not None:
3658 self.else_ = coercions.expect(roles.ExpressionElementRole, else_)
3659 else:
3660 self.else_ = None
3661
3662 type_ = next(
3663 (
3664 then.type
3665 # Iterate `whens` in reverse to match previous behaviour
3666 # where type of final element took priority
3667 for *_, then in reversed(self.whens)
3668 if not then.type._isnull
3669 ),
3670 self.else_.type if self.else_ is not None else type_api.NULLTYPE,
3671 )
3672 self.type = cast(_T, type_)
3673
3674 @util.ro_non_memoized_property
3675 def _from_objects(self) -> List[FromClause]:
3676 return list(
3677 itertools.chain(*[x._from_objects for x in self.get_children()])
3678 )
3679
3680
3681class Cast(WrapsColumnExpression[_T]):
3682 """Represent a ``CAST`` expression.
3683
3684 :class:`.Cast` is produced using the :func:`.cast` factory function,
3685 as in::
3686
3687 from sqlalchemy import cast, Numeric
3688
3689 stmt = select(cast(product_table.c.unit_price, Numeric(10, 4)))
3690
3691 Details on :class:`.Cast` usage is at :func:`.cast`.
3692
3693 .. seealso::
3694
3695 :ref:`tutorial_casts`
3696
3697 :func:`.cast`
3698
3699 :func:`.try_cast`
3700
3701 :func:`.type_coerce` - an alternative to CAST that coerces the type
3702 on the Python side only, which is often sufficient to generate the
3703 correct SQL and data coercion.
3704
3705 """
3706
3707 __visit_name__ = "cast"
3708
3709 _traverse_internals: _TraverseInternalsType = [
3710 ("clause", InternalTraversal.dp_clauseelement),
3711 ("type", InternalTraversal.dp_type),
3712 ]
3713
3714 clause: ColumnElement[Any]
3715 type: TypeEngine[_T]
3716 typeclause: TypeClause
3717
3718 def __init__(
3719 self,
3720 expression: _ColumnExpressionArgument[Any],
3721 type_: _TypeEngineArgument[_T],
3722 ):
3723 self.type = type_api.to_instance(type_)
3724 self.clause = coercions.expect(
3725 roles.ExpressionElementRole,
3726 expression,
3727 type_=self.type,
3728 apply_propagate_attrs=self,
3729 )
3730 self.typeclause = TypeClause(self.type)
3731
3732 @util.ro_non_memoized_property
3733 def _from_objects(self) -> List[FromClause]:
3734 return self.clause._from_objects
3735
3736 @property
3737 def wrapped_column_expression(self):
3738 return self.clause
3739
3740
3741class TryCast(Cast[_T]):
3742 """Represent a TRY_CAST expression.
3743
3744 Details on :class:`.TryCast` usage is at :func:`.try_cast`.
3745
3746 .. seealso::
3747
3748 :func:`.try_cast`
3749
3750 :ref:`tutorial_casts`
3751 """
3752
3753 __visit_name__ = "try_cast"
3754 inherit_cache = True
3755
3756
3757class TypeCoerce(WrapsColumnExpression[_T]):
3758 """Represent a Python-side type-coercion wrapper.
3759
3760 :class:`.TypeCoerce` supplies the :func:`_expression.type_coerce`
3761 function; see that function for usage details.
3762
3763 .. seealso::
3764
3765 :func:`_expression.type_coerce`
3766
3767 :func:`.cast`
3768
3769 """
3770
3771 __visit_name__ = "type_coerce"
3772
3773 _traverse_internals: _TraverseInternalsType = [
3774 ("clause", InternalTraversal.dp_clauseelement),
3775 ("type", InternalTraversal.dp_type),
3776 ]
3777
3778 clause: ColumnElement[Any]
3779 type: TypeEngine[_T]
3780
3781 def __init__(
3782 self,
3783 expression: _ColumnExpressionArgument[Any],
3784 type_: _TypeEngineArgument[_T],
3785 ):
3786 self.type = type_api.to_instance(type_)
3787 self.clause = coercions.expect(
3788 roles.ExpressionElementRole,
3789 expression,
3790 type_=self.type,
3791 apply_propagate_attrs=self,
3792 )
3793
3794 @util.ro_non_memoized_property
3795 def _from_objects(self) -> List[FromClause]:
3796 return self.clause._from_objects
3797
3798 @HasMemoized.memoized_attribute
3799 def typed_expression(self):
3800 if isinstance(self.clause, BindParameter):
3801 bp = self.clause._clone()
3802 bp.type = self.type
3803 return bp
3804 else:
3805 return self.clause
3806
3807 @property
3808 def wrapped_column_expression(self):
3809 return self.clause
3810
3811 def self_group(
3812 self, against: Optional[OperatorType] = None
3813 ) -> TypeCoerce[_T]:
3814 grouped = self.clause.self_group(against=against)
3815 if grouped is not self.clause:
3816 return TypeCoerce(grouped, self.type)
3817 else:
3818 return self
3819
3820
3821class Extract(ColumnElement[int]):
3822 """Represent a SQL EXTRACT clause, ``extract(field FROM expr)``."""
3823
3824 __visit_name__ = "extract"
3825
3826 _traverse_internals: _TraverseInternalsType = [
3827 ("expr", InternalTraversal.dp_clauseelement),
3828 ("field", InternalTraversal.dp_string),
3829 ]
3830
3831 expr: ColumnElement[Any]
3832 field: str
3833
3834 def __init__(self, field: str, expr: _ColumnExpressionArgument[Any]):
3835 self.type = type_api.INTEGERTYPE
3836 self.field = field
3837 self.expr = coercions.expect(roles.ExpressionElementRole, expr)
3838
3839 @util.ro_non_memoized_property
3840 def _from_objects(self) -> List[FromClause]:
3841 return self.expr._from_objects
3842
3843
3844class _label_reference(ColumnElement[_T]):
3845 """Wrap a column expression as it appears in a 'reference' context.
3846
3847 This expression is any that includes an _order_by_label_element,
3848 which is a Label, or a DESC / ASC construct wrapping a Label.
3849
3850 The production of _label_reference() should occur when an expression
3851 is added to this context; this includes the ORDER BY or GROUP BY of a
3852 SELECT statement, as well as a few other places, such as the ORDER BY
3853 within an OVER clause.
3854
3855 """
3856
3857 __visit_name__ = "label_reference"
3858
3859 _traverse_internals: _TraverseInternalsType = [
3860 ("element", InternalTraversal.dp_clauseelement)
3861 ]
3862
3863 element: ColumnElement[_T]
3864
3865 def __init__(self, element: ColumnElement[_T]):
3866 self.element = element
3867 self._propagate_attrs = element._propagate_attrs
3868
3869 @util.ro_non_memoized_property
3870 def _from_objects(self) -> List[FromClause]:
3871 return []
3872
3873
3874class _textual_label_reference(ColumnElement[Any]):
3875 __visit_name__ = "textual_label_reference"
3876
3877 _traverse_internals: _TraverseInternalsType = [
3878 ("element", InternalTraversal.dp_string)
3879 ]
3880
3881 def __init__(self, element: str):
3882 self.element = element
3883
3884 @util.memoized_property
3885 def _text_clause(self) -> TextClause:
3886 return TextClause(self.element)
3887
3888
3889class UnaryExpression(ColumnElement[_T]):
3890 """Define a 'unary' expression.
3891
3892 A unary expression has a single column expression
3893 and an operator. The operator can be placed on the left
3894 (where it is called the 'operator') or right (where it is called the
3895 'modifier') of the column expression.
3896
3897 :class:`.UnaryExpression` is the basis for several unary operators
3898 including those used by :func:`.desc`, :func:`.asc`, :func:`.distinct`,
3899 :func:`.nulls_first` and :func:`.nulls_last`.
3900
3901 """
3902
3903 __visit_name__ = "unary"
3904
3905 _traverse_internals: _TraverseInternalsType = [
3906 ("element", InternalTraversal.dp_clauseelement),
3907 ("operator", InternalTraversal.dp_operator),
3908 ("modifier", InternalTraversal.dp_operator),
3909 ]
3910
3911 element: ColumnElement[Any]
3912 operator: Optional[OperatorType]
3913 modifier: Optional[OperatorType]
3914
3915 def __init__(
3916 self,
3917 element: ColumnElement[Any],
3918 *,
3919 operator: Optional[OperatorType] = None,
3920 modifier: Optional[OperatorType] = None,
3921 type_: Optional[_TypeEngineArgument[_T]] = None,
3922 wraps_column_expression: bool = False, # legacy, not used as of 2.0.42
3923 ):
3924 self.operator = operator
3925 self.modifier = modifier
3926 self._propagate_attrs = element._propagate_attrs
3927 self.element = element.self_group(
3928 against=self.operator or self.modifier
3929 )
3930
3931 # if type is None, we get NULLTYPE, which is our _T. But I don't
3932 # know how to get the overloads to express that correctly
3933 self.type = type_api.to_instance(type_) # type: ignore
3934
3935 def _wraps_unnamed_column(self):
3936 ungrouped = self.element._ungroup()
3937 return (
3938 not isinstance(ungrouped, NamedColumn)
3939 or ungrouped._non_anon_label is None
3940 )
3941
3942 @classmethod
3943 def _create_nulls_first(
3944 cls,
3945 column: _ColumnExpressionArgument[_T],
3946 ) -> UnaryExpression[_T]:
3947 return UnaryExpression(
3948 coercions.expect(roles.ByOfRole, column),
3949 modifier=operators.nulls_first_op,
3950 )
3951
3952 @classmethod
3953 def _create_nulls_last(
3954 cls,
3955 column: _ColumnExpressionArgument[_T],
3956 ) -> UnaryExpression[_T]:
3957 return UnaryExpression(
3958 coercions.expect(roles.ByOfRole, column),
3959 modifier=operators.nulls_last_op,
3960 )
3961
3962 @classmethod
3963 def _create_desc(
3964 cls, column: _ColumnExpressionOrStrLabelArgument[_T]
3965 ) -> UnaryExpression[_T]:
3966
3967 return UnaryExpression(
3968 coercions.expect(roles.ByOfRole, column),
3969 modifier=operators.desc_op,
3970 )
3971
3972 @classmethod
3973 def _create_asc(
3974 cls,
3975 column: _ColumnExpressionOrStrLabelArgument[_T],
3976 ) -> UnaryExpression[_T]:
3977 return UnaryExpression(
3978 coercions.expect(roles.ByOfRole, column),
3979 modifier=operators.asc_op,
3980 )
3981
3982 @classmethod
3983 def _create_distinct(
3984 cls,
3985 expr: _ColumnExpressionArgument[_T],
3986 ) -> UnaryExpression[_T]:
3987 col_expr: ColumnElement[_T] = coercions.expect(
3988 roles.ExpressionElementRole, expr
3989 )
3990 return UnaryExpression(
3991 col_expr,
3992 operator=operators.distinct_op,
3993 type_=col_expr.type,
3994 )
3995
3996 @classmethod
3997 def _create_bitwise_not(
3998 cls,
3999 expr: _ColumnExpressionArgument[_T],
4000 ) -> UnaryExpression[_T]:
4001 col_expr: ColumnElement[_T] = coercions.expect(
4002 roles.ExpressionElementRole, expr
4003 )
4004 return UnaryExpression(
4005 col_expr,
4006 operator=operators.bitwise_not_op,
4007 type_=col_expr.type,
4008 )
4009
4010 @property
4011 def _order_by_label_element(self) -> Optional[Label[Any]]:
4012 if operators.is_order_by_modifier(self.modifier):
4013 return self.element._order_by_label_element
4014 else:
4015 return None
4016
4017 @util.ro_non_memoized_property
4018 def _from_objects(self) -> List[FromClause]:
4019 return self.element._from_objects
4020
4021 def _negate(self) -> ColumnElement[Any]:
4022 if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity:
4023 return UnaryExpression(
4024 self.self_group(against=operators.inv),
4025 operator=operators.inv,
4026 type_=type_api.BOOLEANTYPE,
4027 )
4028 else:
4029 return ColumnElement._negate(self)
4030
4031 def self_group(
4032 self, against: Optional[OperatorType] = None
4033 ) -> Union[Self, Grouping[_T]]:
4034 if self.operator and operators.is_precedent(self.operator, against):
4035 return Grouping(self)
4036 else:
4037 return self
4038
4039
4040class CollectionAggregate(UnaryExpression[_T]):
4041 """Forms the basis for right-hand collection operator modifiers
4042 ANY and ALL.
4043
4044 The ANY and ALL keywords are available in different ways on different
4045 backends. On PostgreSQL, they only work for an ARRAY type. On
4046 MySQL, they only work for subqueries.
4047
4048 """
4049
4050 inherit_cache = True
4051 _is_collection_aggregate = True
4052
4053 @classmethod
4054 def _create_any(
4055 cls, expr: _ColumnExpressionArgument[_T]
4056 ) -> CollectionAggregate[bool]:
4057 """create CollectionAggregate for the legacy
4058 ARRAY.Comparator.any() method"""
4059 col_expr: ColumnElement[_T] = coercions.expect(
4060 roles.ExpressionElementRole,
4061 expr,
4062 )
4063 col_expr = col_expr.self_group()
4064 return CollectionAggregate(
4065 col_expr,
4066 operator=operators.any_op,
4067 type_=type_api.BOOLEANTYPE,
4068 )
4069
4070 @classmethod
4071 def _create_all(
4072 cls, expr: _ColumnExpressionArgument[_T]
4073 ) -> CollectionAggregate[bool]:
4074 """create CollectionAggregate for the legacy
4075 ARRAY.Comparator.all() method"""
4076 col_expr: ColumnElement[_T] = coercions.expect(
4077 roles.ExpressionElementRole,
4078 expr,
4079 )
4080 col_expr = col_expr.self_group()
4081 return CollectionAggregate(
4082 col_expr,
4083 operator=operators.all_op,
4084 type_=type_api.BOOLEANTYPE,
4085 )
4086
4087 @util.preload_module("sqlalchemy.sql.sqltypes")
4088 def _bind_param(
4089 self,
4090 operator: operators.OperatorType,
4091 obj: Any,
4092 type_: Optional[TypeEngine[_T]] = None,
4093 expanding: bool = False,
4094 ) -> BindParameter[_T]:
4095 """For new style any_(), all_(), ensure compared literal value
4096 receives appropriate bound parameter type."""
4097
4098 # a CollectionAggregate is specific to ARRAY or int
4099 # only. So for ARRAY case, make sure we use correct element type
4100 sqltypes = util.preloaded.sql_sqltypes
4101 if self.element.type._type_affinity is sqltypes.ARRAY:
4102 compared_to_type = cast(
4103 sqltypes.ARRAY[Any], self.element.type
4104 ).item_type
4105 else:
4106 compared_to_type = self.element.type
4107
4108 return BindParameter(
4109 None,
4110 obj,
4111 _compared_to_operator=operator,
4112 type_=type_,
4113 _compared_to_type=compared_to_type,
4114 unique=True,
4115 expanding=expanding,
4116 )
4117
4118 # operate and reverse_operate are hardwired to
4119 # dispatch onto the type comparator directly, so that we can
4120 # ensure "reversed" behavior.
4121 def operate(
4122 self, op: OperatorType, *other: Any, **kwargs: Any
4123 ) -> ColumnElement[_T]:
4124 if not operators.is_comparison(op):
4125 raise exc.ArgumentError(
4126 "Only comparison operators may be used with ANY/ALL"
4127 )
4128 kwargs["reverse"] = True
4129 return self.comparator.operate(operators.mirror(op), *other, **kwargs)
4130
4131 def reverse_operate(
4132 self, op: OperatorType, other: Any, **kwargs: Any
4133 ) -> ColumnElement[_T]:
4134 # comparison operators should never call reverse_operate
4135 assert not operators.is_comparison(op)
4136 raise exc.ArgumentError(
4137 "Only comparison operators may be used with ANY/ALL"
4138 )
4139
4140
4141class AsBoolean(WrapsColumnExpression[bool], UnaryExpression[bool]):
4142 inherit_cache = True
4143
4144 def __init__(self, element, operator, negate):
4145 self.element = element
4146 self.type = type_api.BOOLEANTYPE
4147 self.operator = operator
4148 self.negate = negate
4149 self.modifier = None
4150 self._is_implicitly_boolean = element._is_implicitly_boolean
4151
4152 @property
4153 def wrapped_column_expression(self):
4154 return self.element
4155
4156 def self_group(self, against: Optional[OperatorType] = None) -> Self:
4157 return self
4158
4159 def _negate(self):
4160 if isinstance(self.element, (True_, False_)):
4161 return self.element._negate()
4162 else:
4163 return AsBoolean(self.element, self.negate, self.operator)
4164
4165
4166class BinaryExpression(OperatorExpression[_T]):
4167 """Represent an expression that is ``LEFT <operator> RIGHT``.
4168
4169 A :class:`.BinaryExpression` is generated automatically
4170 whenever two column expressions are used in a Python binary expression:
4171
4172 .. sourcecode:: pycon+sql
4173
4174 >>> from sqlalchemy.sql import column
4175 >>> column("a") + column("b")
4176 <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0>
4177 >>> print(column("a") + column("b"))
4178 {printsql}a + b
4179
4180 """
4181
4182 __visit_name__ = "binary"
4183
4184 _traverse_internals: _TraverseInternalsType = [
4185 ("left", InternalTraversal.dp_clauseelement),
4186 ("right", InternalTraversal.dp_clauseelement),
4187 ("operator", InternalTraversal.dp_operator),
4188 ("negate", InternalTraversal.dp_operator),
4189 ("modifiers", InternalTraversal.dp_plain_dict),
4190 (
4191 "type",
4192 InternalTraversal.dp_type,
4193 ),
4194 ]
4195
4196 _cache_key_traversal = [
4197 ("left", InternalTraversal.dp_clauseelement),
4198 ("right", InternalTraversal.dp_clauseelement),
4199 ("operator", InternalTraversal.dp_operator),
4200 ("modifiers", InternalTraversal.dp_plain_dict),
4201 # "type" affects JSON CAST operators, so while redundant in most cases,
4202 # is needed for that one
4203 (
4204 "type",
4205 InternalTraversal.dp_type,
4206 ),
4207 ]
4208
4209 _is_implicitly_boolean = True
4210 """Indicates that any database will know this is a boolean expression
4211 even if the database does not have an explicit boolean datatype.
4212
4213 """
4214
4215 left: ColumnElement[Any]
4216 right: ColumnElement[Any]
4217 modifiers: Mapping[str, Any]
4218
4219 def __init__(
4220 self,
4221 left: ColumnElement[Any],
4222 right: ColumnElement[Any],
4223 operator: OperatorType,
4224 type_: Optional[_TypeEngineArgument[_T]] = None,
4225 negate: Optional[OperatorType] = None,
4226 modifiers: Optional[Mapping[str, Any]] = None,
4227 ):
4228 # allow compatibility with libraries that
4229 # refer to BinaryExpression directly and pass strings
4230 if isinstance(operator, str):
4231 operator = operators.custom_op(operator)
4232 self._orig = (left.__hash__(), right.__hash__())
4233 self._propagate_attrs = left._propagate_attrs or right._propagate_attrs
4234 self.left = left.self_group(against=operator)
4235 self.right = right.self_group(against=operator)
4236 self.operator = operator
4237
4238 # if type is None, we get NULLTYPE, which is our _T. But I don't
4239 # know how to get the overloads to express that correctly
4240 self.type = type_api.to_instance(type_) # type: ignore
4241
4242 self.negate = negate
4243 self._is_implicitly_boolean = operators.is_boolean(operator)
4244
4245 if modifiers is None:
4246 self.modifiers = {}
4247 else:
4248 self.modifiers = modifiers
4249
4250 @property
4251 def _flattened_operator_clauses(
4252 self,
4253 ) -> typing_Tuple[ColumnElement[Any], ...]:
4254 return (self.left, self.right)
4255
4256 def __bool__(self):
4257 """Implement Python-side "bool" for BinaryExpression as a
4258 simple "identity" check for the left and right attributes,
4259 if the operator is "eq" or "ne". Otherwise the expression
4260 continues to not support "bool" like all other column expressions.
4261
4262 The rationale here is so that ColumnElement objects can be hashable.
4263 What? Well, suppose you do this::
4264
4265 c1, c2 = column("x"), column("y")
4266 s1 = set([c1, c2])
4267
4268 We do that **a lot**, columns inside of sets is an extremely basic
4269 thing all over the ORM for example.
4270
4271 So what happens if we do this? ::
4272
4273 c1 in s1
4274
4275 Hashing means it will normally use ``__hash__()`` of the object,
4276 but in case of hash collision, it's going to also do ``c1 == c1``
4277 and/or ``c1 == c2`` inside. Those operations need to return a
4278 True/False value. But because we override ``==`` and ``!=``, they're
4279 going to get a BinaryExpression. Hence we implement ``__bool__`` here
4280 so that these comparisons behave in this particular context mostly
4281 like regular object comparisons. Thankfully Python is OK with
4282 that! Otherwise we'd have to use special set classes for columns
4283 (which we used to do, decades ago).
4284
4285 """
4286 if self.operator in (operators.eq, operators.ne):
4287 # this is using the eq/ne operator given int hash values,
4288 # rather than Operator, so that "bool" can be based on
4289 # identity
4290 return self.operator(*self._orig) # type: ignore
4291 else:
4292 raise TypeError("Boolean value of this clause is not defined")
4293
4294 if typing.TYPE_CHECKING:
4295
4296 def __invert__(
4297 self: BinaryExpression[_T],
4298 ) -> BinaryExpression[_T]: ...
4299
4300 @util.ro_non_memoized_property
4301 def _from_objects(self) -> List[FromClause]:
4302 return self.left._from_objects + self.right._from_objects
4303
4304 def _negate(self):
4305 if self.negate is not None:
4306 return BinaryExpression(
4307 self.left,
4308 self.right._negate_in_binary(self.negate, self.operator),
4309 self.negate,
4310 negate=self.operator,
4311 type_=self.type,
4312 modifiers=self.modifiers,
4313 )
4314 else:
4315 return self.self_group()._negate()
4316
4317
4318class Slice(ColumnElement[Any]):
4319 """Represent SQL for a Python array-slice object.
4320
4321 This is not a specific SQL construct at this level, but
4322 may be interpreted by specific dialects, e.g. PostgreSQL.
4323
4324 """
4325
4326 __visit_name__ = "slice"
4327
4328 _traverse_internals: _TraverseInternalsType = [
4329 ("start", InternalTraversal.dp_clauseelement),
4330 ("stop", InternalTraversal.dp_clauseelement),
4331 ("step", InternalTraversal.dp_clauseelement),
4332 ]
4333
4334 def __init__(self, start, stop, step, _name=None):
4335 self.start = coercions.expect(
4336 roles.ExpressionElementRole,
4337 start,
4338 name=_name,
4339 type_=type_api.INTEGERTYPE,
4340 )
4341 self.stop = coercions.expect(
4342 roles.ExpressionElementRole,
4343 stop,
4344 name=_name,
4345 type_=type_api.INTEGERTYPE,
4346 )
4347 self.step = coercions.expect(
4348 roles.ExpressionElementRole,
4349 step,
4350 name=_name,
4351 type_=type_api.INTEGERTYPE,
4352 )
4353 self.type = type_api.NULLTYPE
4354
4355 def self_group(self, against: Optional[OperatorType] = None) -> Self:
4356 assert against is operator.getitem
4357 return self
4358
4359
4360class IndexExpression(BinaryExpression[Any]):
4361 """Represent the class of expressions that are like an "index"
4362 operation."""
4363
4364 inherit_cache = True
4365
4366
4367class GroupedElement(DQLDMLClauseElement):
4368 """Represent any parenthesized expression"""
4369
4370 __visit_name__ = "grouping"
4371
4372 def self_group(self, against: Optional[OperatorType] = None) -> Self:
4373 return self
4374
4375 def _ungroup(self) -> ClauseElement:
4376 raise NotImplementedError()
4377
4378
4379class Grouping(GroupedElement, ColumnElement[_T]):
4380 """Represent a grouping within a column expression"""
4381
4382 _traverse_internals: _TraverseInternalsType = [
4383 ("element", InternalTraversal.dp_clauseelement),
4384 ("type", InternalTraversal.dp_type),
4385 ]
4386
4387 _cache_key_traversal = [
4388 ("element", InternalTraversal.dp_clauseelement),
4389 ]
4390
4391 element: Union[
4392 AbstractTextClause,
4393 ClauseList,
4394 ColumnElement[_T],
4395 CompilerColumnElement,
4396 ]
4397
4398 def __init__(
4399 self,
4400 element: Union[
4401 AbstractTextClause,
4402 ClauseList,
4403 ColumnElement[_T],
4404 CompilerColumnElement,
4405 ],
4406 ):
4407 self.element = element
4408
4409 # nulltype assignment issue
4410 self.type = getattr(element, "type", type_api.NULLTYPE) # type: ignore
4411 self._propagate_attrs = element._propagate_attrs
4412
4413 def _with_binary_element_type(self, type_):
4414 return self.__class__(self.element._with_binary_element_type(type_))
4415
4416 def _ungroup(self) -> ColumnElement[_T]:
4417 assert isinstance(self.element, ColumnElement)
4418 return self.element._ungroup()
4419
4420 @util.memoized_property
4421 def _is_implicitly_boolean(self):
4422 return self.element._is_implicitly_boolean
4423
4424 @util.non_memoized_property
4425 def _tq_label(self) -> Optional[str]:
4426 return (
4427 getattr(self.element, "_tq_label", None) or self._anon_name_label
4428 )
4429
4430 @util.non_memoized_property
4431 def _proxies(self) -> List[ColumnElement[Any]]:
4432 if isinstance(self.element, ColumnElement):
4433 return [self.element]
4434 else:
4435 return []
4436
4437 @util.ro_non_memoized_property
4438 def _from_objects(self) -> List[FromClause]:
4439 return self.element._from_objects
4440
4441 def __getattr__(self, attr):
4442 return getattr(self.element, attr)
4443
4444 def __getstate__(self):
4445 return {"element": self.element, "type": self.type}
4446
4447 def __setstate__(self, state):
4448 self.element = state["element"]
4449 self.type = state["type"]
4450
4451 if TYPE_CHECKING:
4452
4453 def self_group(
4454 self, against: Optional[OperatorType] = None
4455 ) -> Self: ...
4456
4457
4458class _OverrideBinds(Grouping[_T]):
4459 """used by cache_key->_apply_params_to_element to allow compilation /
4460 execution of a SQL element that's been cached, using an alternate set of
4461 bound parameter values.
4462
4463 This is used by the ORM to swap new parameter values into expressions
4464 that are embedded into loader options like with_expression(),
4465 selectinload(). Previously, this task was accomplished using the
4466 .params() method which would perform a deep-copy instead. This deep
4467 copy proved to be too expensive for more complex expressions.
4468
4469 See #11085
4470
4471 """
4472
4473 __visit_name__ = "override_binds"
4474
4475 def __init__(
4476 self,
4477 element: ColumnElement[_T],
4478 bindparams: Sequence[BindParameter[Any]],
4479 replaces_params: Sequence[BindParameter[Any]],
4480 ):
4481 self.element = element
4482 self.translate = {
4483 k.key: v.value for k, v in zip(replaces_params, bindparams)
4484 }
4485
4486 def _gen_cache_key(
4487 self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
4488 ) -> Optional[typing_Tuple[Any, ...]]:
4489 """generate a cache key for the given element, substituting its bind
4490 values for the translation values present."""
4491
4492 existing_bps: List[BindParameter[Any]] = []
4493 ck = self.element._gen_cache_key(anon_map, existing_bps)
4494
4495 bindparams.extend(
4496 (
4497 bp._with_value(
4498 self.translate[bp.key], maintain_key=True, required=False
4499 )
4500 if bp.key in self.translate
4501 else bp
4502 )
4503 for bp in existing_bps
4504 )
4505
4506 return ck
4507
4508
4509_FrameIntTuple = tuple[int | None, int | None]
4510
4511
4512class Over(ColumnElement[_T]):
4513 """Represent an OVER clause.
4514
4515 This is a special operator against a so-called
4516 "window" function, as well as any aggregate function,
4517 which produces results relative to the result set
4518 itself. Most modern SQL backends now support window functions.
4519
4520 """
4521
4522 __visit_name__ = "over"
4523
4524 _traverse_internals: _TraverseInternalsType = [
4525 ("element", InternalTraversal.dp_clauseelement),
4526 ("order_by", InternalTraversal.dp_clauseelement),
4527 ("partition_by", InternalTraversal.dp_clauseelement),
4528 ("range_", InternalTraversal.dp_clauseelement),
4529 ("rows", InternalTraversal.dp_clauseelement),
4530 ("groups", InternalTraversal.dp_clauseelement),
4531 ]
4532
4533 order_by: Optional[ClauseList] = None
4534 partition_by: Optional[ClauseList] = None
4535
4536 element: ColumnElement[_T]
4537 """The underlying expression object to which this :class:`.Over`
4538 object refers."""
4539
4540 range_: FrameClause | None
4541 rows: FrameClause | None
4542 groups: FrameClause | None
4543
4544 def __init__(
4545 self,
4546 element: ColumnElement[_T],
4547 partition_by: Optional[_ByArgument] = None,
4548 order_by: Optional[_ByArgument] = None,
4549 range_: _FrameIntTuple | FrameClause | None = None,
4550 rows: _FrameIntTuple | FrameClause | None = None,
4551 groups: _FrameIntTuple | FrameClause | None = None,
4552 ):
4553 self.element = element
4554 if order_by is not None:
4555 self.order_by = ClauseList(
4556 *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole
4557 )
4558 if partition_by is not None:
4559 self.partition_by = ClauseList(
4560 *util.to_list(partition_by),
4561 _literal_as_text_role=roles.ByOfRole,
4562 )
4563
4564 if sum(item is not None for item in (range_, rows, groups)) > 1:
4565 raise exc.ArgumentError(
4566 "only one of 'rows', 'range_', or 'groups' may be provided"
4567 )
4568 else:
4569 self.range_ = FrameClause._parse(range_, coerce_int=False)
4570 self.rows = FrameClause._parse(rows, coerce_int=True)
4571 self.groups = FrameClause._parse(groups, coerce_int=True)
4572
4573 if not TYPE_CHECKING:
4574
4575 @util.memoized_property
4576 def type(self) -> TypeEngine[_T]: # noqa: A001
4577 return self.element.type
4578
4579 @util.ro_non_memoized_property
4580 def _from_objects(self) -> List[FromClause]:
4581 return list(
4582 itertools.chain(
4583 *[
4584 c._from_objects
4585 for c in (self.element, self.partition_by, self.order_by)
4586 if c is not None
4587 ]
4588 )
4589 )
4590
4591
4592class FrameClauseType(Enum):
4593 """Frame clause type enum for FrameClause lower_type and upper_type.
4594
4595 .. versionadded:: 2.1
4596
4597 """
4598
4599 UNBOUNDED = 0
4600 """Produces an "UNBOUNDED PRECEDING" or "UNBOUNDED FOLLOWING" frame
4601 clause depending on the position.
4602 Requires a ``None`` value for the corresponding bound value.
4603 """
4604 CURRENT = 1
4605 """Produces a "CURRENT ROW" frame clause.
4606 Requires a ``None`` value for the corresponding bound value.
4607 """
4608 PRECEDING = 2
4609 """Produces a "PRECEDING" frame clause."""
4610 FOLLOWING = 3
4611 """Produces a "FOLLOWING" frame clause."""
4612
4613
4614_require_none = (
4615 FrameClauseType.CURRENT,
4616 FrameClauseType.UNBOUNDED,
4617)
4618
4619
4620class FrameClause(ClauseElement):
4621 """Indicate the 'rows' 'range' or 'group' field of a window function,
4622 e.g. using :class:`.Over`.
4623
4624 .. versionadded:: 2.1
4625
4626 """
4627
4628 __visit_name__ = "frame_clause"
4629
4630 _traverse_internals: _TraverseInternalsType = [
4631 ("lower_bind", InternalTraversal.dp_clauseelement),
4632 ("upper_bind", InternalTraversal.dp_clauseelement),
4633 ("lower_type", InternalTraversal.dp_plain_obj),
4634 ("upper_type", InternalTraversal.dp_plain_obj),
4635 ]
4636
4637 def __init__(
4638 self,
4639 start: Any,
4640 end: Any,
4641 start_frame_type: FrameClauseType,
4642 end_frame_type: FrameClauseType,
4643 _validate: bool = True,
4644 ) -> None:
4645 """Creates a new FrameClause specifying the bounds of a window frame.
4646
4647 :param start: The start value.
4648 :param end: The end value.
4649 :param start_frame_type: The :class:`FrameClauseType` for the
4650 start value.
4651 :param end_frame_type: The :class:`FrameClauseType` for the end value.
4652 """
4653 self.lower_bind = self._as_literal(start)
4654 self.upper_bind = self._as_literal(end)
4655 self.lower_type = FrameClauseType(start_frame_type)
4656 self.upper_type = FrameClauseType(end_frame_type)
4657 if _validate:
4658 if (
4659 self.lower_type in _require_none
4660 and self.lower_bind is not None
4661 ):
4662 raise exc.ArgumentError(
4663 "Cannot specify a value for start with frame type "
4664 f"{self.lower_type.name}"
4665 )
4666 if (
4667 self.upper_type in _require_none
4668 and self.upper_bind is not None
4669 ):
4670 raise exc.ArgumentError(
4671 "Cannot specify a value for end with frame type "
4672 f"{self.upper_type.name}"
4673 )
4674
4675 @classmethod
4676 def _as_literal(cls, value: Any) -> BindParameter[Any] | None:
4677 if value is None:
4678 return None
4679 elif isinstance(value, int):
4680 return literal(value, type_api.INTEGERTYPE)
4681 elif isinstance(value, BindParameter):
4682 return value
4683 else:
4684 return literal(value) # let the default type resolution occur
4685
4686 @classmethod
4687 def _handle_int(
4688 cls, value: Any | None, coerce_int: bool
4689 ) -> tuple[int | None, FrameClauseType]:
4690 if value is None:
4691 return None, FrameClauseType.UNBOUNDED
4692
4693 if coerce_int:
4694 try:
4695 integer = int(value)
4696 except ValueError as err:
4697 raise exc.ArgumentError(
4698 "Integer or None expected for values in rows/groups frame"
4699 ) from err
4700 elif not isinstance(value, int):
4701 raise exc.ArgumentError(
4702 "When using a tuple to specify a range only integer or none "
4703 "values are allowed in the range frame. To specify a "
4704 "different type use the FrameClause directly."
4705 )
4706 else:
4707 integer = value
4708 if integer == 0:
4709 return None, FrameClauseType.CURRENT
4710 elif integer < 0:
4711 return abs(integer), FrameClauseType.PRECEDING
4712 else:
4713 return integer, FrameClauseType.FOLLOWING
4714
4715 @classmethod
4716 def _parse(
4717 cls,
4718 range_: _FrameIntTuple | FrameClause | None,
4719 coerce_int: bool,
4720 ) -> FrameClause | None:
4721 if range_ is None or isinstance(range_, FrameClause):
4722 return range_
4723
4724 try:
4725 r0, r1 = range_
4726 except (ValueError, TypeError) as ve:
4727 raise exc.ArgumentError(
4728 "2-tuple expected for range/rows/groups"
4729 ) from ve
4730
4731 l_b, l_t = cls._handle_int(r0, coerce_int)
4732 u_b, u_t = cls._handle_int(r1, coerce_int)
4733
4734 return FrameClause(
4735 start=l_b,
4736 end=u_b,
4737 start_frame_type=l_t,
4738 end_frame_type=u_t,
4739 _validate=False,
4740 )
4741
4742
4743class AggregateOrderBy(WrapsColumnExpression[_T]):
4744 """Represent an aggregate ORDER BY expression.
4745
4746 This is a special operator against aggregate functions such as
4747 ``array_agg()``, ``json_arrayagg()`` ``string_agg()``, etc. that provides
4748 for an ORDER BY expression, using a syntax that's compatible with
4749 the backend.
4750
4751 :class:`.AggregateOrderBy` is a generalized version of the
4752 :class:`.WithinGroup` construct, the latter of which always provides a
4753 "WITHIN GROUP (ORDER BY ...)" expression. :class:`.AggregateOrderBy` will
4754 also compile to "WITHIN GROUP (ORDER BY ...)" on backends such as Oracle
4755 and SQL Server that don't have another style of aggregate function
4756 ordering.
4757
4758 .. versionadded:: 2.1
4759
4760
4761 """
4762
4763 __visit_name__ = "aggregateorderby"
4764
4765 _traverse_internals: _TraverseInternalsType = [
4766 ("element", InternalTraversal.dp_clauseelement),
4767 ("order_by", InternalTraversal.dp_clauseelement),
4768 ]
4769
4770 order_by: ClauseList
4771
4772 def __init__(
4773 self,
4774 element: Union[FunctionElement[_T], FunctionFilter[_T]],
4775 *order_by: _ColumnExpressionArgument[Any],
4776 ):
4777 self.element = element
4778 if not order_by:
4779 raise TypeError("at least one ORDER BY element is required")
4780 self.order_by = ClauseList(
4781 *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole
4782 )
4783
4784 if not TYPE_CHECKING:
4785
4786 @util.memoized_property
4787 def type(self) -> TypeEngine[_T]: # noqa: A001
4788 return self.element.type
4789
4790 @property
4791 def wrapped_column_expression(self) -> ColumnElement[_T]:
4792 return self.element
4793
4794 def __reduce__(self):
4795 return self.__class__, (self.element,) + (
4796 tuple(self.order_by) if self.order_by is not None else ()
4797 )
4798
4799 def over(
4800 self,
4801 *,
4802 partition_by: _ByArgument | None = None,
4803 order_by: _ByArgument | None = None,
4804 rows: _FrameIntTuple | FrameClause | None = None,
4805 range_: _FrameIntTuple | FrameClause | None = None,
4806 groups: _FrameIntTuple | FrameClause | None = None,
4807 ) -> Over[_T]:
4808 """Produce an OVER clause against this :class:`.WithinGroup`
4809 construct.
4810
4811 This function has the same signature as that of
4812 :meth:`.FunctionElement.over`.
4813
4814 """
4815 return Over(
4816 self,
4817 partition_by=partition_by,
4818 order_by=order_by,
4819 range_=range_,
4820 rows=rows,
4821 groups=groups,
4822 )
4823
4824 @overload
4825 def filter(self) -> Self: ...
4826
4827 @overload
4828 def filter(
4829 self,
4830 __criterion0: _ColumnExpressionArgument[bool],
4831 *criterion: _ColumnExpressionArgument[bool],
4832 ) -> FunctionFilter[_T]: ...
4833
4834 def filter(
4835 self, *criterion: _ColumnExpressionArgument[bool]
4836 ) -> Union[Self, FunctionFilter[_T]]:
4837 """Produce a FILTER clause against this function."""
4838 if not criterion:
4839 return self
4840 return FunctionFilter(self, *criterion)
4841
4842 @util.ro_non_memoized_property
4843 def _from_objects(self) -> List[FromClause]:
4844 return list(
4845 itertools.chain(
4846 *[
4847 c._from_objects
4848 for c in (self.element, self.order_by)
4849 if c is not None
4850 ]
4851 )
4852 )
4853
4854
4855class WithinGroup(AggregateOrderBy[_T]):
4856 """Represent a WITHIN GROUP (ORDER BY) clause.
4857
4858 This is a special operator against so-called
4859 "ordered set aggregate" and "hypothetical
4860 set aggregate" functions, including ``percentile_cont()``,
4861 ``rank()``, ``dense_rank()``, etc.
4862
4863 It's supported only by certain database backends, such as PostgreSQL,
4864 Oracle Database and MS SQL Server.
4865
4866 The :class:`.WithinGroup` construct extracts its type from the
4867 method :meth:`.FunctionElement.within_group_type`. If this returns
4868 ``None``, the function's ``.type`` is used.
4869
4870 """
4871
4872 __visit_name__ = "withingroup"
4873 inherit_cache = True
4874
4875 if not TYPE_CHECKING:
4876
4877 @util.memoized_property
4878 def type(self) -> TypeEngine[_T]: # noqa: A001
4879 wgt = self.element.within_group_type(self)
4880 if wgt is not None:
4881 return wgt
4882 else:
4883 return self.element.type
4884
4885
4886class FunctionFilter(Generative, ColumnElement[_T]):
4887 """Represent a function FILTER clause.
4888
4889 This is a special operator against aggregate and window functions,
4890 which controls which rows are passed to it.
4891 It's supported only by certain database backends.
4892
4893 Invocation of :class:`.FunctionFilter` is via
4894 :meth:`.FunctionElement.filter`::
4895
4896 func.count(1).filter(True)
4897
4898 .. seealso::
4899
4900 :meth:`.FunctionElement.filter`
4901
4902 """
4903
4904 __visit_name__ = "funcfilter"
4905
4906 _traverse_internals: _TraverseInternalsType = [
4907 ("func", InternalTraversal.dp_clauseelement),
4908 ("criterion", InternalTraversal.dp_clauseelement),
4909 ]
4910
4911 criterion: Optional[ColumnElement[bool]] = None
4912
4913 def __init__(
4914 self,
4915 func: Union[FunctionElement[_T], AggregateOrderBy[_T]],
4916 *criterion: _ColumnExpressionArgument[bool],
4917 ):
4918 self.func = func
4919 self.filter.non_generative(self, *criterion) # type: ignore
4920
4921 @_generative
4922 def filter(self, *criterion: _ColumnExpressionArgument[bool]) -> Self:
4923 """Produce an additional FILTER against the function.
4924
4925 This method adds additional criteria to the initial criteria
4926 set up by :meth:`.FunctionElement.filter`.
4927
4928 Multiple criteria are joined together at SQL render time
4929 via ``AND``.
4930
4931
4932 """
4933
4934 for crit in list(criterion):
4935 crit = coercions.expect(roles.WhereHavingRole, crit)
4936
4937 if self.criterion is not None:
4938 self.criterion = self.criterion & crit
4939 else:
4940 self.criterion = crit
4941
4942 return self
4943
4944 def over(
4945 self,
4946 partition_by: _ByArgument | None = None,
4947 order_by: _ByArgument | None = None,
4948 range_: _FrameIntTuple | FrameClause | None = None,
4949 rows: _FrameIntTuple | FrameClause | None = None,
4950 groups: _FrameIntTuple | FrameClause | None = None,
4951 ) -> Over[_T]:
4952 """Produce an OVER clause against this filtered function.
4953
4954 Used against aggregate or so-called "window" functions,
4955 for database backends that support window functions.
4956
4957 The expression::
4958
4959 func.rank().filter(MyClass.y > 5).over(order_by="x")
4960
4961 is shorthand for::
4962
4963 from sqlalchemy import over, funcfilter
4964
4965 over(funcfilter(func.rank(), MyClass.y > 5), order_by="x")
4966
4967 See :func:`_expression.over` for a full description.
4968
4969 """
4970 return Over(
4971 self,
4972 partition_by=partition_by,
4973 order_by=order_by,
4974 range_=range_,
4975 rows=rows,
4976 groups=groups,
4977 )
4978
4979 def within_group(
4980 self, *order_by: _ColumnExpressionArgument[Any]
4981 ) -> WithinGroup[_T]:
4982 """Produce a WITHIN GROUP (ORDER BY expr) clause against
4983 this function.
4984 """
4985 return WithinGroup(self, *order_by)
4986
4987 def within_group_type(
4988 self, within_group: WithinGroup[_T]
4989 ) -> Optional[TypeEngine[_T]]:
4990 return None
4991
4992 def self_group(
4993 self, against: Optional[OperatorType] = None
4994 ) -> Union[Self, Grouping[_T]]:
4995 if operators.is_precedent(operators.filter_op, against):
4996 return Grouping(self)
4997 else:
4998 return self
4999
5000 if not TYPE_CHECKING:
5001
5002 @util.memoized_property
5003 def type(self) -> TypeEngine[_T]: # noqa: A001
5004 return self.func.type
5005
5006 @util.ro_non_memoized_property
5007 def _from_objects(self) -> List[FromClause]:
5008 return list(
5009 itertools.chain(
5010 *[
5011 c._from_objects
5012 for c in (self.func, self.criterion)
5013 if c is not None
5014 ]
5015 )
5016 )
5017
5018
5019class NamedColumn(KeyedColumnElement[_T]):
5020 is_literal = False
5021 table: Optional[FromClause] = None
5022 name: str
5023 key: str
5024
5025 def _compare_name_for_result(self, other):
5026 return (hasattr(other, "name") and self.name == other.name) or (
5027 hasattr(other, "_label") and self._label == other._label
5028 )
5029
5030 @util.ro_memoized_property
5031 def description(self) -> str:
5032 return self.name
5033
5034 @HasMemoized.memoized_attribute
5035 def _tq_key_label(self) -> Optional[str]:
5036 """table qualified label based on column key.
5037
5038 for table-bound columns this is <tablename>_<column key/proxy key>;
5039
5040 all other expressions it resolves to key/proxy key.
5041
5042 """
5043 proxy_key = self._proxy_key
5044 if proxy_key and proxy_key != self.name:
5045 return self._gen_tq_label(proxy_key)
5046 else:
5047 return self._tq_label
5048
5049 @HasMemoized.memoized_attribute
5050 def _tq_label(self) -> Optional[str]:
5051 """table qualified label based on column name.
5052
5053 for table-bound columns this is <tablename>_<columnname>; all other
5054 expressions it resolves to .name.
5055
5056 """
5057 return self._gen_tq_label(self.name)
5058
5059 @HasMemoized.memoized_attribute
5060 def _render_label_in_columns_clause(self):
5061 return True
5062
5063 @HasMemoized.memoized_attribute
5064 def _non_anon_label(self):
5065 return self.name
5066
5067 def _gen_tq_label(
5068 self, name: str, dedupe_on_key: bool = True
5069 ) -> Optional[str]:
5070 return name
5071
5072 def _bind_param(
5073 self,
5074 operator: OperatorType,
5075 obj: Any,
5076 type_: Optional[TypeEngine[_T]] = None,
5077 expanding: bool = False,
5078 ) -> BindParameter[_T]:
5079 return BindParameter(
5080 self.key,
5081 obj,
5082 _compared_to_operator=operator,
5083 _compared_to_type=self.type,
5084 type_=type_,
5085 unique=True,
5086 expanding=expanding,
5087 )
5088
5089 def _make_proxy(
5090 self,
5091 selectable: FromClause,
5092 *,
5093 primary_key: ColumnSet,
5094 foreign_keys: Set[KeyedColumnElement[Any]],
5095 name: Optional[str] = None,
5096 key: Optional[str] = None,
5097 name_is_truncatable: bool = False,
5098 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5099 disallow_is_literal: bool = False,
5100 **kw: Any,
5101 ) -> typing_Tuple[str, ColumnClause[_T]]:
5102 c = ColumnClause(
5103 (
5104 coercions.expect(roles.TruncatedLabelRole, name or self.name)
5105 if name_is_truncatable
5106 else (name or self.name)
5107 ),
5108 type_=self.type,
5109 _selectable=selectable,
5110 is_literal=False,
5111 )
5112
5113 c._propagate_attrs = selectable._propagate_attrs
5114 if name is None:
5115 c.key = self.key
5116 if compound_select_cols:
5117 c._proxies = list(compound_select_cols)
5118 else:
5119 c._proxies = [self]
5120
5121 if selectable._is_clone_of is not None:
5122 c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
5123 return c.key, c
5124
5125
5126_PS = ParamSpec("_PS")
5127
5128
5129class Label(roles.LabeledColumnExprRole[_T], NamedColumn[_T]):
5130 """Represents a column label (AS).
5131
5132 Represent a label, as typically applied to any column-level
5133 element using the ``AS`` sql keyword.
5134
5135 """
5136
5137 __visit_name__ = "label"
5138
5139 _traverse_internals: _TraverseInternalsType = [
5140 ("name", InternalTraversal.dp_anon_name),
5141 ("type", InternalTraversal.dp_type),
5142 ("_element", InternalTraversal.dp_clauseelement),
5143 ]
5144
5145 _cache_key_traversal = [
5146 ("name", InternalTraversal.dp_anon_name),
5147 ("_element", InternalTraversal.dp_clauseelement),
5148 ]
5149
5150 _element: ColumnElement[_T]
5151 name: str
5152
5153 def __init__(
5154 self,
5155 name: Optional[str],
5156 element: _ColumnExpressionArgument[_T],
5157 type_: Optional[_TypeEngineArgument[_T]] = None,
5158 ):
5159 orig_element = element
5160 element = coercions.expect(
5161 roles.ExpressionElementRole,
5162 element,
5163 apply_propagate_attrs=self,
5164 )
5165 while isinstance(element, Label):
5166 # TODO: this is only covered in test_text.py, but nothing
5167 # fails if it's removed. determine rationale
5168 element = element.element
5169
5170 if name:
5171 self.name = name
5172 else:
5173 self.name = _anonymous_label.safe_construct(
5174 id(self), getattr(element, "name", "anon")
5175 )
5176 if isinstance(orig_element, Label):
5177 # TODO: no coverage for this block, again would be in
5178 # test_text.py where the resolve_label concept is important
5179 self._resolve_label = orig_element._label
5180
5181 self.key = self._tq_label = self._tq_key_label = self.name
5182 self._element = element
5183
5184 self.type = (
5185 type_api.to_instance(type_)
5186 if type_ is not None
5187 else self._element.type
5188 )
5189
5190 self._proxies = [element]
5191
5192 def __reduce__(self):
5193 return self.__class__, (self.name, self._element, self.type)
5194
5195 @HasMemoized.memoized_attribute
5196 def _render_label_in_columns_clause(self):
5197 return True
5198
5199 def _bind_param(self, operator, obj, type_=None, expanding=False):
5200 return BindParameter(
5201 None,
5202 obj,
5203 _compared_to_operator=operator,
5204 type_=type_,
5205 _compared_to_type=self.type,
5206 unique=True,
5207 expanding=expanding,
5208 )
5209
5210 @util.memoized_property
5211 def _is_implicitly_boolean(self):
5212 return self.element._is_implicitly_boolean
5213
5214 @HasMemoized.memoized_attribute
5215 def _allow_label_resolve(self):
5216 return self.element._allow_label_resolve
5217
5218 @property
5219 def _order_by_label_element(self):
5220 return self
5221
5222 def as_reference(self) -> _label_reference[_T]:
5223 """refer to this labeled expression in a clause such as GROUP BY,
5224 ORDER BY etc. as the label name itself, without expanding
5225 into the full expression.
5226
5227 .. versionadded:: 2.1
5228
5229 """
5230 return _label_reference(self)
5231
5232 @HasMemoized.memoized_attribute
5233 def element(self) -> ColumnElement[_T]:
5234 return self._element.self_group(against=operators.as_)
5235
5236 def self_group(self, against: Optional[OperatorType] = None) -> Label[_T]:
5237 return self._apply_to_inner(self._element.self_group, against=against)
5238
5239 def _negate(self):
5240 return self._apply_to_inner(self._element._negate)
5241
5242 def _apply_to_inner(
5243 self,
5244 fn: Callable[_PS, ColumnElement[_T]],
5245 *arg: _PS.args,
5246 **kw: _PS.kwargs,
5247 ) -> Label[_T]:
5248 sub_element = fn(*arg, **kw)
5249 if sub_element is not self._element:
5250 return Label(self.name, sub_element, type_=self.type)
5251 else:
5252 return self
5253
5254 @property
5255 def primary_key(self): # type: ignore[override]
5256 return self.element.primary_key
5257
5258 @property
5259 def foreign_keys(self): # type: ignore[override]
5260 return self.element.foreign_keys
5261
5262 def _copy_internals(
5263 self,
5264 *,
5265 clone: _CloneCallableType = _clone,
5266 anonymize_labels: bool = False,
5267 **kw: Any,
5268 ) -> None:
5269 self._reset_memoizations()
5270 self._element = clone(self._element, **kw)
5271 if anonymize_labels:
5272 self.name = _anonymous_label.safe_construct(
5273 id(self), getattr(self.element, "name", "anon")
5274 )
5275 self.key = self._tq_label = self._tq_key_label = self.name
5276
5277 @util.ro_non_memoized_property
5278 def _from_objects(self) -> List[FromClause]:
5279 return self.element._from_objects
5280
5281 def _make_proxy(
5282 self,
5283 selectable: FromClause,
5284 *,
5285 primary_key: ColumnSet,
5286 foreign_keys: Set[KeyedColumnElement[Any]],
5287 name: Optional[str] = None,
5288 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5289 **kw: Any,
5290 ) -> typing_Tuple[str, ColumnClause[_T]]:
5291 name = self.name if not name else name
5292
5293 key, e = self.element._make_proxy(
5294 selectable,
5295 name=name,
5296 disallow_is_literal=True,
5297 name_is_truncatable=isinstance(name, _truncated_label),
5298 compound_select_cols=compound_select_cols,
5299 primary_key=primary_key,
5300 foreign_keys=foreign_keys,
5301 )
5302
5303 # there was a note here to remove this assertion, which was here
5304 # to determine if we later could support a use case where
5305 # the key and name of a label are separate. But I don't know what
5306 # that case was. For now, this is an unexpected case that occurs
5307 # when a label name conflicts with other columns and select()
5308 # is attempting to disambiguate an explicit label, which is not what
5309 # the user would want. See issue #6090.
5310 if key != self.name and not isinstance(self.name, _anonymous_label):
5311 raise exc.InvalidRequestError(
5312 "Label name %s is being renamed to an anonymous label due "
5313 "to disambiguation "
5314 "which is not supported right now. Please use unique names "
5315 "for explicit labels." % (self.name)
5316 )
5317
5318 e._propagate_attrs = selectable._propagate_attrs
5319 e._proxies.append(self)
5320 if self.type is not None:
5321 e.type = self.type
5322
5323 return self.key, e
5324
5325
5326class ColumnClause(
5327 roles.DDLReferredColumnRole,
5328 roles.LabeledColumnExprRole[_T],
5329 roles.StrAsPlainColumnRole,
5330 Immutable,
5331 NamedColumn[_T],
5332):
5333 """Represents a column expression from any textual string.
5334
5335 The :class:`.ColumnClause`, a lightweight analogue to the
5336 :class:`_schema.Column` class, is typically invoked using the
5337 :func:`_expression.column` function, as in::
5338
5339 from sqlalchemy import column
5340
5341 id, name = column("id"), column("name")
5342 stmt = select(id, name).select_from("user")
5343
5344 The above statement would produce SQL like:
5345
5346 .. sourcecode:: sql
5347
5348 SELECT id, name FROM user
5349
5350 :class:`.ColumnClause` is the immediate superclass of the schema-specific
5351 :class:`_schema.Column` object. While the :class:`_schema.Column`
5352 class has all the
5353 same capabilities as :class:`.ColumnClause`, the :class:`.ColumnClause`
5354 class is usable by itself in those cases where behavioral requirements
5355 are limited to simple SQL expression generation. The object has none of
5356 the associations with schema-level metadata or with execution-time
5357 behavior that :class:`_schema.Column` does,
5358 so in that sense is a "lightweight"
5359 version of :class:`_schema.Column`.
5360
5361 Full details on :class:`.ColumnClause` usage is at
5362 :func:`_expression.column`.
5363
5364 .. seealso::
5365
5366 :func:`_expression.column`
5367
5368 :class:`_schema.Column`
5369
5370 """
5371
5372 table: Optional[FromClause]
5373 is_literal: bool
5374
5375 __visit_name__ = "column"
5376
5377 _traverse_internals: _TraverseInternalsType = [
5378 ("name", InternalTraversal.dp_anon_name),
5379 ("type", InternalTraversal.dp_type),
5380 ("table", InternalTraversal.dp_clauseelement),
5381 ("is_literal", InternalTraversal.dp_boolean),
5382 ]
5383
5384 onupdate: Optional[DefaultGenerator] = None
5385 default: Optional[DefaultGenerator] = None
5386 server_default: Optional[FetchedValue] = None
5387 server_onupdate: Optional[FetchedValue] = None
5388
5389 _is_multiparam_column = False
5390
5391 @property
5392 def _is_star(self): # type: ignore[override]
5393 return self.is_literal and self.name == "*"
5394
5395 def __init__(
5396 self,
5397 text: str,
5398 type_: Optional[_TypeEngineArgument[_T]] = None,
5399 is_literal: bool = False,
5400 _selectable: Optional[FromClause] = None,
5401 ):
5402 self.key = self.name = text
5403 self.table = _selectable
5404
5405 # if type is None, we get NULLTYPE, which is our _T. But I don't
5406 # know how to get the overloads to express that correctly
5407 self.type = type_api.to_instance(type_) # type: ignore
5408
5409 self.is_literal = is_literal
5410
5411 def get_children(self, *, column_tables=False, **kw):
5412 # override base get_children() to not return the Table
5413 # or selectable that is parent to this column. Traversals
5414 # expect the columns of tables and subqueries to be leaf nodes.
5415 return []
5416
5417 @property
5418 def entity_namespace(self):
5419 if self.table is not None:
5420 return self.table.entity_namespace
5421 else:
5422 return super().entity_namespace
5423
5424 def _clone(self, detect_subquery_cols=False, **kw):
5425 if (
5426 detect_subquery_cols
5427 and self.table is not None
5428 and self.table._is_subquery
5429 ):
5430 clone = kw.pop("clone")
5431 table = clone(self.table, **kw)
5432 new = table.c.corresponding_column(self)
5433 return new
5434
5435 return super()._clone(**kw)
5436
5437 @HasMemoized_ro_memoized_attribute
5438 def _from_objects(self) -> List[FromClause]:
5439 t = self.table
5440 if t is not None:
5441 return [t]
5442 else:
5443 return []
5444
5445 @HasMemoized.memoized_attribute
5446 def _render_label_in_columns_clause(self):
5447 return self.table is not None
5448
5449 @property
5450 def _ddl_label(self):
5451 return self._gen_tq_label(self.name, dedupe_on_key=False)
5452
5453 def _compare_name_for_result(self, other):
5454 if (
5455 self.is_literal
5456 or self.table is None
5457 or self.table._is_textual
5458 or not hasattr(other, "proxy_set")
5459 or (
5460 isinstance(other, ColumnClause)
5461 and (
5462 other.is_literal
5463 or other.table is None
5464 or other.table._is_textual
5465 )
5466 )
5467 ):
5468 return (hasattr(other, "name") and self.name == other.name) or (
5469 hasattr(other, "_tq_label")
5470 and self._tq_label == other._tq_label
5471 )
5472 else:
5473 return other.proxy_set.intersection(self.proxy_set)
5474
5475 def _gen_tq_label(
5476 self, name: str, dedupe_on_key: bool = True
5477 ) -> Optional[str]:
5478 """generate table-qualified label
5479
5480 for a table-bound column this is <tablename>_<columnname>.
5481
5482 used primarily for LABEL_STYLE_TABLENAME_PLUS_COL
5483 as well as the .columns collection on a Join object.
5484
5485 """
5486 label: str
5487 t = self.table
5488 if self.is_literal:
5489 return None
5490 elif t is not None and is_named_from_clause(t):
5491 if has_schema_attr(t) and t.schema:
5492 label = (
5493 t.schema.replace(".", "_") + "_" + t.name + ("_" + name)
5494 )
5495 else:
5496 assert not TYPE_CHECKING or isinstance(t, NamedFromClause)
5497 label = t.name + ("_" + name)
5498
5499 # propagate name quoting rules for labels.
5500 if is_quoted_name(name) and name.quote is not None:
5501 if is_quoted_name(label):
5502 label.quote = name.quote
5503 else:
5504 label = quoted_name(label, name.quote)
5505 elif is_quoted_name(t.name) and t.name.quote is not None:
5506 # can't get this situation to occur, so let's
5507 # assert false on it for now
5508 assert not isinstance(label, quoted_name)
5509 label = quoted_name(label, t.name.quote)
5510
5511 if dedupe_on_key:
5512 # ensure the label name doesn't conflict with that of an
5513 # existing column. note that this implies that any Column
5514 # must **not** set up its _label before its parent table has
5515 # all of its other Column objects set up. There are several
5516 # tables in the test suite which will fail otherwise; example:
5517 # table "owner" has columns "name" and "owner_name". Therefore
5518 # column owner.name cannot use the label "owner_name", it has
5519 # to be "owner_name_1".
5520 if label in t.c:
5521 _label = label
5522 counter = 1
5523 while _label in t.c:
5524 _label = label + f"_{counter}"
5525 counter += 1
5526 label = _label
5527
5528 return coercions.expect(roles.TruncatedLabelRole, label)
5529
5530 else:
5531 return name
5532
5533 def _make_proxy(
5534 self,
5535 selectable: FromClause,
5536 *,
5537 primary_key: ColumnSet,
5538 foreign_keys: Set[KeyedColumnElement[Any]],
5539 name: Optional[str] = None,
5540 key: Optional[str] = None,
5541 name_is_truncatable: bool = False,
5542 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5543 disallow_is_literal: bool = False,
5544 **kw: Any,
5545 ) -> typing_Tuple[str, ColumnClause[_T]]:
5546 # the "is_literal" flag normally should never be propagated; a proxied
5547 # column is always a SQL identifier and never the actual expression
5548 # being evaluated. however, there is a case where the "is_literal" flag
5549 # might be used to allow the given identifier to have a fixed quoting
5550 # pattern already, so maintain the flag for the proxy unless a
5551 # :class:`.Label` object is creating the proxy. See [ticket:4730].
5552 is_literal = (
5553 not disallow_is_literal
5554 and self.is_literal
5555 and (
5556 # note this does not accommodate for quoted_name differences
5557 # right now
5558 name is None
5559 or name == self.name
5560 )
5561 )
5562 c = self._constructor(
5563 (
5564 coercions.expect(roles.TruncatedLabelRole, name or self.name)
5565 if name_is_truncatable
5566 else (name or self.name)
5567 ),
5568 type_=self.type,
5569 _selectable=selectable,
5570 is_literal=is_literal,
5571 )
5572 c._propagate_attrs = selectable._propagate_attrs
5573 if name is None:
5574 c.key = self.key
5575 if compound_select_cols:
5576 c._proxies = list(compound_select_cols)
5577 else:
5578 c._proxies = [self]
5579
5580 if selectable._is_clone_of is not None:
5581 c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
5582 return c.key, c
5583
5584
5585class TableValuedColumn(NamedColumn[_T]):
5586 __visit_name__ = "table_valued_column"
5587
5588 _traverse_internals: _TraverseInternalsType = [
5589 ("name", InternalTraversal.dp_anon_name),
5590 ("type", InternalTraversal.dp_type),
5591 ("scalar_alias", InternalTraversal.dp_clauseelement),
5592 ]
5593
5594 def __init__(self, scalar_alias: NamedFromClause, type_: TypeEngine[_T]):
5595 self.scalar_alias = scalar_alias
5596 self.key = self.name = scalar_alias.name
5597 self.type = type_
5598
5599 def _copy_internals(
5600 self, clone: _CloneCallableType = _clone, **kw: Any
5601 ) -> None:
5602 self.scalar_alias = clone(self.scalar_alias, **kw)
5603 self.key = self.name = self.scalar_alias.name
5604
5605 @util.ro_non_memoized_property
5606 def _from_objects(self) -> List[FromClause]:
5607 return [self.scalar_alias]
5608
5609
5610class CollationClause(ColumnElement[str]):
5611 __visit_name__ = "collation"
5612
5613 _traverse_internals: _TraverseInternalsType = [
5614 ("collation", InternalTraversal.dp_string)
5615 ]
5616
5617 @classmethod
5618 @util.preload_module("sqlalchemy.sql.sqltypes")
5619 def _create_collation_expression(
5620 cls, expression: _ColumnExpressionArgument[str], collation: str
5621 ) -> BinaryExpression[str]:
5622
5623 sqltypes = util.preloaded.sql_sqltypes
5624
5625 expr = coercions.expect(roles.ExpressionElementRole[str], expression)
5626
5627 if expr.type._type_affinity is sqltypes.String:
5628 collate_type = expr.type._with_collation(collation)
5629 else:
5630 collate_type = expr.type
5631
5632 return BinaryExpression(
5633 expr,
5634 CollationClause(collation),
5635 operators.collate,
5636 type_=collate_type,
5637 )
5638
5639 def __init__(self, collation):
5640 self.collation = collation
5641
5642
5643class _IdentifiedClause(Executable, ClauseElement):
5644 __visit_name__ = "identified"
5645
5646 def __init__(self, ident):
5647 self.ident = ident
5648
5649
5650class SavepointClause(_IdentifiedClause):
5651 __visit_name__ = "savepoint"
5652 inherit_cache = False
5653
5654
5655class RollbackToSavepointClause(_IdentifiedClause):
5656 __visit_name__ = "rollback_to_savepoint"
5657 inherit_cache = False
5658
5659
5660class ReleaseSavepointClause(_IdentifiedClause):
5661 __visit_name__ = "release_savepoint"
5662 inherit_cache = False
5663
5664
5665class quoted_name(util.MemoizedSlots, str):
5666 """Represent a SQL identifier combined with quoting preferences.
5667
5668 :class:`.quoted_name` is a Python unicode/str subclass which
5669 represents a particular identifier name along with a
5670 ``quote`` flag. This ``quote`` flag, when set to
5671 ``True`` or ``False``, overrides automatic quoting behavior
5672 for this identifier in order to either unconditionally quote
5673 or to not quote the name. If left at its default of ``None``,
5674 quoting behavior is applied to the identifier on a per-backend basis
5675 based on an examination of the token itself.
5676
5677 A :class:`.quoted_name` object with ``quote=True`` is also
5678 prevented from being modified in the case of a so-called
5679 "name normalize" option. Certain database backends, such as
5680 Oracle Database, Firebird, and DB2 "normalize" case-insensitive names
5681 as uppercase. The SQLAlchemy dialects for these backends
5682 convert from SQLAlchemy's lower-case-means-insensitive convention
5683 to the upper-case-means-insensitive conventions of those backends.
5684 The ``quote=True`` flag here will prevent this conversion from occurring
5685 to support an identifier that's quoted as all lower case against
5686 such a backend.
5687
5688 The :class:`.quoted_name` object is normally created automatically
5689 when specifying the name for key schema constructs such as
5690 :class:`_schema.Table`, :class:`_schema.Column`, and others.
5691 The class can also be
5692 passed explicitly as the name to any function that receives a name which
5693 can be quoted. Such as to use the :meth:`_engine.Engine.has_table`
5694 method with
5695 an unconditionally quoted name::
5696
5697 from sqlalchemy import create_engine
5698 from sqlalchemy import inspect
5699 from sqlalchemy.sql import quoted_name
5700
5701 engine = create_engine("oracle+oracledb://some_dsn")
5702 print(inspect(engine).has_table(quoted_name("some_table", True)))
5703
5704 The above logic will run the "has table" logic against the Oracle Database
5705 backend, passing the name exactly as ``"some_table"`` without converting to
5706 upper case.
5707
5708 """
5709
5710 __slots__ = "quote", "lower", "upper"
5711
5712 quote: Optional[bool]
5713
5714 @overload
5715 @classmethod
5716 def construct(cls, value: str, quote: Optional[bool]) -> quoted_name: ...
5717
5718 @overload
5719 @classmethod
5720 def construct(cls, value: None, quote: Optional[bool]) -> None: ...
5721
5722 @classmethod
5723 def construct(
5724 cls, value: Optional[str], quote: Optional[bool]
5725 ) -> Optional[quoted_name]:
5726 if value is None:
5727 return None
5728 else:
5729 return quoted_name(value, quote)
5730
5731 def __new__(cls, value: str, quote: Optional[bool]) -> quoted_name:
5732 assert (
5733 value is not None
5734 ), "use quoted_name.construct() for None passthrough"
5735 if isinstance(value, cls) and (quote is None or value.quote == quote):
5736 return value
5737 self = super().__new__(cls, value)
5738
5739 self.quote = quote
5740 return self
5741
5742 def __reduce__(self):
5743 return quoted_name, (str(self), self.quote)
5744
5745 def _memoized_method_lower(self):
5746 if self.quote:
5747 return self
5748 else:
5749 return str(self).lower()
5750
5751 def _memoized_method_upper(self):
5752 if self.quote:
5753 return self
5754 else:
5755 return str(self).upper()
5756
5757
5758def _find_columns(clause: ClauseElement) -> Set[ColumnClause[Any]]:
5759 """locate Column objects within the given expression."""
5760
5761 cols: Set[ColumnClause[Any]] = set()
5762 traverse(clause, {}, {"column": cols.add})
5763 return cols
5764
5765
5766def _type_from_args(args: Sequence[ColumnElement[_T]]) -> TypeEngine[_T]:
5767 for a in args:
5768 if not a.type._isnull:
5769 return a.type
5770 else:
5771 return type_api.NULLTYPE # type: ignore
5772
5773
5774def _corresponding_column_or_error(fromclause, column, require_embedded=False):
5775 c = fromclause.corresponding_column(
5776 column, require_embedded=require_embedded
5777 )
5778 if c is None:
5779 raise exc.InvalidRequestError(
5780 "Given column '%s', attached to table '%s', "
5781 "failed to locate a corresponding column from table '%s'"
5782 % (column, getattr(column, "table", None), fromclause.description)
5783 )
5784 return c
5785
5786
5787class _memoized_property_but_not_nulltype(
5788 util.memoized_property["TypeEngine[_T]"]
5789):
5790 """memoized property, but dont memoize NullType"""
5791
5792 def __get__(self, obj, cls):
5793 if obj is None:
5794 return self
5795 result = self.fget(obj)
5796 if not result._isnull:
5797 obj.__dict__[self.__name__] = result
5798 return result
5799
5800
5801class AnnotatedColumnElement(Annotated):
5802 _Annotated__element: ColumnElement[Any]
5803
5804 def __init__(self, element, values):
5805 Annotated.__init__(self, element, values)
5806 for attr in (
5807 "comparator",
5808 "_proxy_key",
5809 "_tq_key_label",
5810 "_tq_label",
5811 "_non_anon_label",
5812 "type",
5813 ):
5814 self.__dict__.pop(attr, None)
5815 for attr in ("name", "key", "table"):
5816 if self.__dict__.get(attr, False) is None:
5817 self.__dict__.pop(attr)
5818
5819 def _with_annotations(self, values):
5820 clone = super()._with_annotations(values)
5821 for attr in (
5822 "comparator",
5823 "_proxy_key",
5824 "_tq_key_label",
5825 "_tq_label",
5826 "_non_anon_label",
5827 ):
5828 clone.__dict__.pop(attr, None)
5829 return clone
5830
5831 @util.memoized_property
5832 def name(self):
5833 """pull 'name' from parent, if not present"""
5834 return self._Annotated__element.name
5835
5836 @_memoized_property_but_not_nulltype
5837 def type(self):
5838 """pull 'type' from parent and don't cache if null.
5839
5840 type is routinely changed on existing columns within the
5841 mapped_column() initialization process, and "type" is also consulted
5842 during the creation of SQL expressions. Therefore it can change after
5843 it was already retrieved. At the same time we don't want annotated
5844 objects having overhead when expressions are produced, so continue
5845 to memoize, but only when we have a non-null type.
5846
5847 """
5848 return self._Annotated__element.type
5849
5850 @util.memoized_property
5851 def table(self):
5852 """pull 'table' from parent, if not present"""
5853 return self._Annotated__element.table
5854
5855 @util.memoized_property
5856 def key(self):
5857 """pull 'key' from parent, if not present"""
5858 return self._Annotated__element.key
5859
5860 @util.memoized_property
5861 def info(self) -> _InfoType:
5862 if TYPE_CHECKING:
5863 assert isinstance(self._Annotated__element, Column)
5864 return self._Annotated__element.info
5865
5866 @util.memoized_property
5867 def _anon_name_label(self) -> str:
5868 return self._Annotated__element._anon_name_label
5869
5870
5871class _truncated_label(quoted_name):
5872 """A unicode subclass used to identify symbolic "
5873 "names that may require truncation."""
5874
5875 __slots__ = ()
5876
5877 def __new__(cls, value: str, quote: Optional[bool] = None) -> Any:
5878 quote = getattr(value, "quote", quote)
5879 # return super(_truncated_label, cls).__new__(cls, value, quote, True)
5880 return super().__new__(cls, value, quote)
5881
5882 def __reduce__(self) -> Any:
5883 return self.__class__, (str(self), self.quote)
5884
5885 def apply_map(self, map_: Mapping[str, Any]) -> str:
5886 return self
5887
5888
5889class conv(_truncated_label):
5890 """Mark a string indicating that a name has already been converted
5891 by a naming convention.
5892
5893 This is a string subclass that indicates a name that should not be
5894 subject to any further naming conventions.
5895
5896 E.g. when we create a :class:`.Constraint` using a naming convention
5897 as follows::
5898
5899 m = MetaData(
5900 naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}
5901 )
5902 t = Table(
5903 "t", m, Column("x", Integer), CheckConstraint("x > 5", name="x5")
5904 )
5905
5906 The name of the above constraint will be rendered as ``"ck_t_x5"``.
5907 That is, the existing name ``x5`` is used in the naming convention as the
5908 ``constraint_name`` token.
5909
5910 In some situations, such as in migration scripts, we may be rendering
5911 the above :class:`.CheckConstraint` with a name that's already been
5912 converted. In order to make sure the name isn't double-modified, the
5913 new name is applied using the :func:`_schema.conv` marker. We can
5914 use this explicitly as follows::
5915
5916
5917 m = MetaData(
5918 naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}
5919 )
5920 t = Table(
5921 "t",
5922 m,
5923 Column("x", Integer),
5924 CheckConstraint("x > 5", name=conv("ck_t_x5")),
5925 )
5926
5927 Where above, the :func:`_schema.conv` marker indicates that the constraint
5928 name here is final, and the name will render as ``"ck_t_x5"`` and not
5929 ``"ck_t_ck_t_x5"``
5930
5931 .. seealso::
5932
5933 :ref:`constraint_naming_conventions`
5934
5935 """
5936
5937 __slots__ = ()
5938
5939
5940# for backwards compatibility in case
5941# someone is re-implementing the
5942# _truncated_identifier() sequence in a custom
5943# compiler
5944_generated_label = _truncated_label
5945_anonymous_label_escape = re.compile(r"[%\(\) \$]+")
5946
5947
5948class _anonymous_label(_truncated_label):
5949 """A unicode subclass used to identify anonymously
5950 generated names."""
5951
5952 __slots__ = ()
5953
5954 @classmethod
5955 def safe_construct_with_key(
5956 cls, seed: int, body: str, sanitize_key: bool = False
5957 ) -> typing_Tuple[_anonymous_label, str]:
5958 # need to escape chars that interfere with format
5959 # strings in any case, issue #8724
5960 body = _anonymous_label_escape.sub("_", body)
5961
5962 if sanitize_key:
5963 # sanitize_key is then an extra step used by BindParameter
5964 body = body.strip("_")
5965
5966 key = f"{seed} {body.replace('%', '%%')}"
5967 label = _anonymous_label(f"%({key})s")
5968 return label, key
5969
5970 @classmethod
5971 def safe_construct(
5972 cls, seed: int, body: str, sanitize_key: bool = False
5973 ) -> _anonymous_label:
5974 # need to escape chars that interfere with format
5975 # strings in any case, issue #8724
5976 body = _anonymous_label_escape.sub("_", body)
5977
5978 if sanitize_key:
5979 # sanitize_key is then an extra step used by BindParameter
5980 body = body.strip("_")
5981
5982 return _anonymous_label(f"%({seed} {body.replace('%', '%%')})s")
5983
5984 def __add__(self, other: str) -> _anonymous_label:
5985 if "%" in other and not isinstance(other, _anonymous_label):
5986 other = str(other).replace("%", "%%")
5987 else:
5988 other = str(other)
5989
5990 return _anonymous_label(
5991 quoted_name(
5992 str.__add__(self, other),
5993 self.quote,
5994 )
5995 )
5996
5997 def __radd__(self, other: str) -> _anonymous_label:
5998 if "%" in other and not isinstance(other, _anonymous_label):
5999 other = str(other).replace("%", "%%")
6000 else:
6001 other = str(other)
6002
6003 return _anonymous_label(
6004 quoted_name(
6005 str.__add__(other, self),
6006 self.quote,
6007 )
6008 )
6009
6010 def apply_map(self, map_: Mapping[str, Any]) -> str:
6011 if self.quote is not None:
6012 # preserve quoting only if necessary
6013 return quoted_name(self % map_, self.quote)
6014 else:
6015 # else skip the constructor call
6016 return self % map_