1# sql/elements.py
2# Copyright (C) 2005-2026 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 Optional[_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 reuse 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 ("exclude", InternalTraversal.dp_string),
4532 ]
4533
4534 order_by: Optional[ClauseList] = None
4535 partition_by: Optional[ClauseList] = None
4536
4537 element: ColumnElement[_T]
4538 """The underlying expression object to which this :class:`.Over`
4539 object refers."""
4540
4541 range_: FrameClause | None
4542 rows: FrameClause | None
4543 groups: FrameClause | None
4544 exclude: str | None
4545
4546 def __init__(
4547 self,
4548 element: ColumnElement[_T],
4549 partition_by: Optional[_ByArgument] = None,
4550 order_by: Optional[_ByArgument] = None,
4551 range_: _FrameIntTuple | FrameClause | None = None,
4552 rows: _FrameIntTuple | FrameClause | None = None,
4553 groups: _FrameIntTuple | FrameClause | None = None,
4554 exclude: str | None = None,
4555 ):
4556 self.element = element
4557 if order_by is not None:
4558 self.order_by = ClauseList(
4559 *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole
4560 )
4561 if partition_by is not None:
4562 self.partition_by = ClauseList(
4563 *util.to_list(partition_by),
4564 _literal_as_text_role=roles.ByOfRole,
4565 )
4566
4567 if sum(item is not None for item in (range_, rows, groups)) > 1:
4568 raise exc.ArgumentError(
4569 "only one of 'rows', 'range_', or 'groups' may be provided"
4570 )
4571 else:
4572 self.range_ = FrameClause._parse(range_, coerce_int=False)
4573 self.rows = FrameClause._parse(rows, coerce_int=True)
4574 self.groups = FrameClause._parse(groups, coerce_int=True)
4575
4576 self.exclude = exclude
4577
4578 if exclude is not None and (
4579 range_ is None and rows is None and groups is None
4580 ):
4581 raise exc.ArgumentError(
4582 "'exclude' requires that one of 'rows', "
4583 "'range_', or 'groups' is also specified"
4584 )
4585
4586 if not TYPE_CHECKING:
4587
4588 @util.memoized_property
4589 def type(self) -> TypeEngine[_T]: # noqa: A001
4590 return self.element.type
4591
4592 @util.ro_non_memoized_property
4593 def _from_objects(self) -> List[FromClause]:
4594 return list(
4595 itertools.chain(
4596 *[
4597 c._from_objects
4598 for c in (self.element, self.partition_by, self.order_by)
4599 if c is not None
4600 ]
4601 )
4602 )
4603
4604
4605class FrameClauseType(Enum):
4606 """Frame clause type enum for FrameClause lower_type and upper_type.
4607
4608 .. versionadded:: 2.1
4609
4610 """
4611
4612 UNBOUNDED = 0
4613 """Produces an "UNBOUNDED PRECEDING" or "UNBOUNDED FOLLOWING" frame
4614 clause depending on the position.
4615 Requires a ``None`` value for the corresponding bound value.
4616 """
4617 CURRENT = 1
4618 """Produces a "CURRENT ROW" frame clause.
4619 Requires a ``None`` value for the corresponding bound value.
4620 """
4621 PRECEDING = 2
4622 """Produces a "PRECEDING" frame clause."""
4623 FOLLOWING = 3
4624 """Produces a "FOLLOWING" frame clause."""
4625
4626
4627_require_none = (
4628 FrameClauseType.CURRENT,
4629 FrameClauseType.UNBOUNDED,
4630)
4631
4632
4633class FrameClause(ClauseElement):
4634 """Indicate the 'rows' 'range' or 'group' field of a window function,
4635 e.g. using :class:`.Over`.
4636
4637 .. versionadded:: 2.1
4638
4639 """
4640
4641 __visit_name__ = "frame_clause"
4642
4643 _traverse_internals: _TraverseInternalsType = [
4644 ("lower_bind", InternalTraversal.dp_clauseelement),
4645 ("upper_bind", InternalTraversal.dp_clauseelement),
4646 ("lower_type", InternalTraversal.dp_plain_obj),
4647 ("upper_type", InternalTraversal.dp_plain_obj),
4648 ]
4649
4650 def __init__(
4651 self,
4652 start: Any,
4653 end: Any,
4654 start_frame_type: FrameClauseType,
4655 end_frame_type: FrameClauseType,
4656 _validate: bool = True,
4657 ) -> None:
4658 """Creates a new FrameClause specifying the bounds of a window frame.
4659
4660 :param start: The start value.
4661 :param end: The end value.
4662 :param start_frame_type: The :class:`FrameClauseType` for the
4663 start value.
4664 :param end_frame_type: The :class:`FrameClauseType` for the end value.
4665 """
4666 self.lower_bind = self._as_literal(start)
4667 self.upper_bind = self._as_literal(end)
4668 self.lower_type = FrameClauseType(start_frame_type)
4669 self.upper_type = FrameClauseType(end_frame_type)
4670 if _validate:
4671 if (
4672 self.lower_type in _require_none
4673 and self.lower_bind is not None
4674 ):
4675 raise exc.ArgumentError(
4676 "Cannot specify a value for start with frame type "
4677 f"{self.lower_type.name}"
4678 )
4679 if (
4680 self.upper_type in _require_none
4681 and self.upper_bind is not None
4682 ):
4683 raise exc.ArgumentError(
4684 "Cannot specify a value for end with frame type "
4685 f"{self.upper_type.name}"
4686 )
4687
4688 @classmethod
4689 def _as_literal(cls, value: Any) -> BindParameter[Any] | None:
4690 if value is None:
4691 return None
4692 elif isinstance(value, int):
4693 return literal(value, type_api.INTEGERTYPE)
4694 elif isinstance(value, BindParameter):
4695 return value
4696 else:
4697 return literal(value) # let the default type resolution occur
4698
4699 @classmethod
4700 def _handle_int(
4701 cls, value: Any | None, coerce_int: bool
4702 ) -> tuple[int | None, FrameClauseType]:
4703 if value is None:
4704 return None, FrameClauseType.UNBOUNDED
4705
4706 if coerce_int:
4707 try:
4708 integer = int(value)
4709 except ValueError as err:
4710 raise exc.ArgumentError(
4711 "Integer or None expected for values in rows/groups frame"
4712 ) from err
4713 elif not isinstance(value, int):
4714 raise exc.ArgumentError(
4715 "When using a tuple to specify a range only integer or none "
4716 "values are allowed in the range frame. To specify a "
4717 "different type use the FrameClause directly."
4718 )
4719 else:
4720 integer = value
4721 if integer == 0:
4722 return None, FrameClauseType.CURRENT
4723 elif integer < 0:
4724 return abs(integer), FrameClauseType.PRECEDING
4725 else:
4726 return integer, FrameClauseType.FOLLOWING
4727
4728 @classmethod
4729 def _parse(
4730 cls,
4731 range_: _FrameIntTuple | FrameClause | None,
4732 coerce_int: bool,
4733 ) -> FrameClause | None:
4734 if range_ is None or isinstance(range_, FrameClause):
4735 return range_
4736
4737 try:
4738 r0, r1 = range_
4739 except (ValueError, TypeError) as ve:
4740 raise exc.ArgumentError(
4741 "2-tuple expected for range/rows/groups"
4742 ) from ve
4743
4744 l_b, l_t = cls._handle_int(r0, coerce_int)
4745 u_b, u_t = cls._handle_int(r1, coerce_int)
4746
4747 return FrameClause(
4748 start=l_b,
4749 end=u_b,
4750 start_frame_type=l_t,
4751 end_frame_type=u_t,
4752 _validate=False,
4753 )
4754
4755
4756class AggregateOrderBy(WrapsColumnExpression[_T]):
4757 """Represent an aggregate ORDER BY expression.
4758
4759 This is a special operator against aggregate functions such as
4760 ``array_agg()``, ``json_arrayagg()`` ``string_agg()``, etc. that provides
4761 for an ORDER BY expression, using a syntax that's compatible with
4762 the backend.
4763
4764 :class:`.AggregateOrderBy` is a generalized version of the
4765 :class:`.WithinGroup` construct, the latter of which always provides a
4766 "WITHIN GROUP (ORDER BY ...)" expression. :class:`.AggregateOrderBy` will
4767 also compile to "WITHIN GROUP (ORDER BY ...)" on backends such as Oracle
4768 and SQL Server that don't have another style of aggregate function
4769 ordering.
4770
4771 .. versionadded:: 2.1
4772
4773
4774 """
4775
4776 __visit_name__ = "aggregateorderby"
4777
4778 _traverse_internals: _TraverseInternalsType = [
4779 ("element", InternalTraversal.dp_clauseelement),
4780 ("order_by", InternalTraversal.dp_clauseelement),
4781 ]
4782
4783 order_by: ClauseList
4784
4785 def __init__(
4786 self,
4787 element: Union[FunctionElement[_T], FunctionFilter[_T]],
4788 *order_by: _ColumnExpressionArgument[Any],
4789 ):
4790 self.element = element
4791 if not order_by:
4792 raise TypeError("at least one ORDER BY element is required")
4793 self.order_by = ClauseList(
4794 *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole
4795 )
4796
4797 if not TYPE_CHECKING:
4798
4799 @util.memoized_property
4800 def type(self) -> TypeEngine[_T]: # noqa: A001
4801 return self.element.type
4802
4803 @property
4804 def wrapped_column_expression(self) -> ColumnElement[_T]:
4805 return self.element
4806
4807 def __reduce__(self):
4808 return self.__class__, (self.element,) + (
4809 tuple(self.order_by) if self.order_by is not None else ()
4810 )
4811
4812 def over(
4813 self,
4814 *,
4815 partition_by: _ByArgument | None = None,
4816 order_by: _ByArgument | None = None,
4817 rows: _FrameIntTuple | FrameClause | None = None,
4818 range_: _FrameIntTuple | FrameClause | None = None,
4819 groups: _FrameIntTuple | FrameClause | None = None,
4820 exclude: str | None = None,
4821 ) -> Over[_T]:
4822 """Produce an OVER clause against this :class:`.WithinGroup`
4823 construct.
4824
4825 This function has the same signature as that of
4826 :meth:`.FunctionElement.over`.
4827
4828 """
4829 return Over(
4830 self,
4831 partition_by=partition_by,
4832 order_by=order_by,
4833 range_=range_,
4834 rows=rows,
4835 groups=groups,
4836 exclude=exclude,
4837 )
4838
4839 @overload
4840 def filter(self) -> Self: ...
4841
4842 @overload
4843 def filter(
4844 self,
4845 __criterion0: _ColumnExpressionArgument[bool],
4846 *criterion: _ColumnExpressionArgument[bool],
4847 ) -> FunctionFilter[_T]: ...
4848
4849 def filter(
4850 self, *criterion: _ColumnExpressionArgument[bool]
4851 ) -> Union[Self, FunctionFilter[_T]]:
4852 """Produce a FILTER clause against this function."""
4853 if not criterion:
4854 return self
4855 return FunctionFilter(self, *criterion)
4856
4857 @util.ro_non_memoized_property
4858 def _from_objects(self) -> List[FromClause]:
4859 return list(
4860 itertools.chain(
4861 *[
4862 c._from_objects
4863 for c in (self.element, self.order_by)
4864 if c is not None
4865 ]
4866 )
4867 )
4868
4869
4870class WithinGroup(AggregateOrderBy[_T]):
4871 """Represent a WITHIN GROUP (ORDER BY) clause.
4872
4873 This is a special operator against so-called
4874 "ordered set aggregate" and "hypothetical
4875 set aggregate" functions, including ``percentile_cont()``,
4876 ``rank()``, ``dense_rank()``, etc.
4877
4878 It's supported only by certain database backends, such as PostgreSQL,
4879 Oracle Database and MS SQL Server.
4880
4881 The :class:`.WithinGroup` construct extracts its type from the
4882 method :meth:`.FunctionElement.within_group_type`. If this returns
4883 ``None``, the function's ``.type`` is used.
4884
4885 """
4886
4887 __visit_name__ = "withingroup"
4888 inherit_cache = True
4889
4890 if not TYPE_CHECKING:
4891
4892 @util.memoized_property
4893 def type(self) -> TypeEngine[_T]: # noqa: A001
4894 wgt = self.element.within_group_type(self)
4895 if wgt is not None:
4896 return wgt
4897 else:
4898 return self.element.type
4899
4900
4901class FunctionFilter(Generative, ColumnElement[_T]):
4902 """Represent a function FILTER clause.
4903
4904 This is a special operator against aggregate and window functions,
4905 which controls which rows are passed to it.
4906 It's supported only by certain database backends.
4907
4908 Invocation of :class:`.FunctionFilter` is via
4909 :meth:`.FunctionElement.filter`::
4910
4911 func.count(1).filter(True)
4912
4913 .. seealso::
4914
4915 :meth:`.FunctionElement.filter`
4916
4917 """
4918
4919 __visit_name__ = "funcfilter"
4920
4921 _traverse_internals: _TraverseInternalsType = [
4922 ("func", InternalTraversal.dp_clauseelement),
4923 ("criterion", InternalTraversal.dp_clauseelement),
4924 ]
4925
4926 criterion: Optional[ColumnElement[bool]] = None
4927
4928 def __init__(
4929 self,
4930 func: Union[FunctionElement[_T], AggregateOrderBy[_T]],
4931 *criterion: _ColumnExpressionArgument[bool],
4932 ):
4933 self.func = func
4934 self.filter.non_generative(self, *criterion) # type: ignore
4935
4936 @_generative
4937 def filter(self, *criterion: _ColumnExpressionArgument[bool]) -> Self:
4938 """Produce an additional FILTER against the function.
4939
4940 This method adds additional criteria to the initial criteria
4941 set up by :meth:`.FunctionElement.filter`.
4942
4943 Multiple criteria are joined together at SQL render time
4944 via ``AND``.
4945
4946
4947 """
4948
4949 for crit in list(criterion):
4950 crit = coercions.expect(roles.WhereHavingRole, crit)
4951
4952 if self.criterion is not None:
4953 self.criterion = self.criterion & crit
4954 else:
4955 self.criterion = crit
4956
4957 return self
4958
4959 def over(
4960 self,
4961 partition_by: _ByArgument | None = None,
4962 order_by: _ByArgument | None = None,
4963 range_: _FrameIntTuple | FrameClause | None = None,
4964 rows: _FrameIntTuple | FrameClause | None = None,
4965 groups: _FrameIntTuple | FrameClause | None = None,
4966 exclude: str | None = None,
4967 ) -> Over[_T]:
4968 """Produce an OVER clause against this filtered function.
4969
4970 Used against aggregate or so-called "window" functions,
4971 for database backends that support window functions.
4972
4973 The expression::
4974
4975 func.rank().filter(MyClass.y > 5).over(order_by="x")
4976
4977 is shorthand for::
4978
4979 from sqlalchemy import over, funcfilter
4980
4981 over(funcfilter(func.rank(), MyClass.y > 5), order_by="x")
4982
4983 See :func:`_expression.over` for a full description.
4984
4985 """
4986 return Over(
4987 self,
4988 partition_by=partition_by,
4989 order_by=order_by,
4990 range_=range_,
4991 rows=rows,
4992 groups=groups,
4993 exclude=exclude,
4994 )
4995
4996 def within_group(
4997 self, *order_by: _ColumnExpressionArgument[Any]
4998 ) -> WithinGroup[_T]:
4999 """Produce a WITHIN GROUP (ORDER BY expr) clause against
5000 this function.
5001 """
5002 return WithinGroup(self, *order_by)
5003
5004 def within_group_type(
5005 self, within_group: WithinGroup[_T]
5006 ) -> Optional[TypeEngine[_T]]:
5007 return None
5008
5009 def self_group(
5010 self, against: Optional[OperatorType] = None
5011 ) -> Union[Self, Grouping[_T]]:
5012 if operators.is_precedent(operators.filter_op, against):
5013 return Grouping(self)
5014 else:
5015 return self
5016
5017 if not TYPE_CHECKING:
5018
5019 @util.memoized_property
5020 def type(self) -> TypeEngine[_T]: # noqa: A001
5021 return self.func.type
5022
5023 @util.ro_non_memoized_property
5024 def _from_objects(self) -> List[FromClause]:
5025 return list(
5026 itertools.chain(
5027 *[
5028 c._from_objects
5029 for c in (self.func, self.criterion)
5030 if c is not None
5031 ]
5032 )
5033 )
5034
5035
5036class NamedColumn(KeyedColumnElement[_T]):
5037 is_literal = False
5038 table: Optional[FromClause] = None
5039 name: str
5040 key: str
5041
5042 def _compare_name_for_result(self, other):
5043 return (hasattr(other, "name") and self.name == other.name) or (
5044 hasattr(other, "_label") and self._label == other._label
5045 )
5046
5047 @util.ro_memoized_property
5048 def description(self) -> str:
5049 return self.name
5050
5051 @HasMemoized.memoized_attribute
5052 def _tq_key_label(self) -> Optional[str]:
5053 """table qualified label based on column key.
5054
5055 for table-bound columns this is <tablename>_<column key/proxy key>;
5056
5057 all other expressions it resolves to key/proxy key.
5058
5059 """
5060 proxy_key = self._proxy_key
5061 if proxy_key and proxy_key != self.name:
5062 return self._gen_tq_label(proxy_key)
5063 else:
5064 return self._tq_label
5065
5066 @HasMemoized.memoized_attribute
5067 def _tq_label(self) -> Optional[str]:
5068 """table qualified label based on column name.
5069
5070 for table-bound columns this is <tablename>_<columnname>; all other
5071 expressions it resolves to .name.
5072
5073 """
5074 return self._gen_tq_label(self.name)
5075
5076 @HasMemoized.memoized_attribute
5077 def _render_label_in_columns_clause(self):
5078 return True
5079
5080 @HasMemoized.memoized_attribute
5081 def _non_anon_label(self):
5082 return self.name
5083
5084 def _gen_tq_label(
5085 self, name: str, dedupe_on_key: bool = True
5086 ) -> Optional[str]:
5087 return name
5088
5089 def _bind_param(
5090 self,
5091 operator: OperatorType,
5092 obj: Any,
5093 type_: Optional[TypeEngine[_T]] = None,
5094 expanding: bool = False,
5095 ) -> BindParameter[_T]:
5096 return BindParameter(
5097 self.key,
5098 obj,
5099 _compared_to_operator=operator,
5100 _compared_to_type=self.type,
5101 type_=type_,
5102 unique=True,
5103 expanding=expanding,
5104 )
5105
5106 def _make_proxy(
5107 self,
5108 selectable: FromClause,
5109 *,
5110 primary_key: ColumnSet,
5111 foreign_keys: Set[KeyedColumnElement[Any]],
5112 name: Optional[str] = None,
5113 key: Optional[str] = None,
5114 name_is_truncatable: bool = False,
5115 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5116 disallow_is_literal: bool = False,
5117 **kw: Any,
5118 ) -> typing_Tuple[str, ColumnClause[_T]]:
5119 c = ColumnClause(
5120 (
5121 coercions.expect(roles.TruncatedLabelRole, name or self.name)
5122 if name_is_truncatable
5123 else (name or self.name)
5124 ),
5125 type_=self.type,
5126 _selectable=selectable,
5127 is_literal=False,
5128 )
5129
5130 c._propagate_attrs = selectable._propagate_attrs
5131 if name is None:
5132 c.key = self.key
5133 if compound_select_cols:
5134 c._proxies = list(compound_select_cols)
5135 else:
5136 c._proxies = [self]
5137
5138 if selectable._is_clone_of is not None:
5139 c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
5140 return c.key, c
5141
5142
5143_PS = ParamSpec("_PS")
5144
5145
5146class Label(roles.LabeledColumnExprRole[_T], NamedColumn[_T]):
5147 """Represents a column label (AS).
5148
5149 Represent a label, as typically applied to any column-level
5150 element using the ``AS`` sql keyword.
5151
5152 """
5153
5154 __visit_name__ = "label"
5155
5156 _traverse_internals: _TraverseInternalsType = [
5157 ("name", InternalTraversal.dp_anon_name),
5158 ("type", InternalTraversal.dp_type),
5159 ("_element", InternalTraversal.dp_clauseelement),
5160 ]
5161
5162 _cache_key_traversal = [
5163 ("name", InternalTraversal.dp_anon_name),
5164 ("_element", InternalTraversal.dp_clauseelement),
5165 ]
5166
5167 _element: ColumnElement[_T]
5168 name: str
5169
5170 def __init__(
5171 self,
5172 name: Optional[str],
5173 element: _ColumnExpressionArgument[_T],
5174 type_: Optional[_TypeEngineArgument[_T]] = None,
5175 ):
5176 orig_element = element
5177 element = coercions.expect(
5178 roles.ExpressionElementRole,
5179 element,
5180 apply_propagate_attrs=self,
5181 )
5182 while isinstance(element, Label):
5183 # TODO: this is only covered in test_text.py, but nothing
5184 # fails if it's removed. determine rationale
5185 element = element.element
5186
5187 if name:
5188 self.name = name
5189 else:
5190 self.name = _anonymous_label.safe_construct(
5191 id(self), getattr(element, "name", "anon")
5192 )
5193 if isinstance(orig_element, Label):
5194 # TODO: no coverage for this block, again would be in
5195 # test_text.py where the resolve_label concept is important
5196 self._resolve_label = orig_element._label
5197
5198 self.key = self._tq_label = self._tq_key_label = self.name
5199 self._element = element
5200
5201 self.type = (
5202 type_api.to_instance(type_)
5203 if type_ is not None
5204 else self._element.type
5205 )
5206
5207 self._proxies = [element]
5208
5209 def __reduce__(self):
5210 return self.__class__, (self.name, self._element, self.type)
5211
5212 @HasMemoized.memoized_attribute
5213 def _render_label_in_columns_clause(self):
5214 return True
5215
5216 def _bind_param(self, operator, obj, type_=None, expanding=False):
5217 return BindParameter(
5218 None,
5219 obj,
5220 _compared_to_operator=operator,
5221 type_=type_,
5222 _compared_to_type=self.type,
5223 unique=True,
5224 expanding=expanding,
5225 )
5226
5227 @util.memoized_property
5228 def _is_implicitly_boolean(self):
5229 return self.element._is_implicitly_boolean
5230
5231 @HasMemoized.memoized_attribute
5232 def _allow_label_resolve(self):
5233 return self.element._allow_label_resolve
5234
5235 @property
5236 def _order_by_label_element(self):
5237 return self
5238
5239 def as_reference(self) -> _label_reference[_T]:
5240 """refer to this labeled expression in a clause such as GROUP BY,
5241 ORDER BY etc. as the label name itself, without expanding
5242 into the full expression.
5243
5244 .. versionadded:: 2.1
5245
5246 """
5247 return _label_reference(self)
5248
5249 @HasMemoized.memoized_attribute
5250 def element(self) -> ColumnElement[_T]:
5251 return self._element.self_group(against=operators.as_)
5252
5253 def self_group(self, against: Optional[OperatorType] = None) -> Label[_T]:
5254 return self._apply_to_inner(self._element.self_group, against=against)
5255
5256 def _negate(self):
5257 return self._apply_to_inner(self._element._negate)
5258
5259 def _apply_to_inner(
5260 self,
5261 fn: Callable[_PS, ColumnElement[_T]],
5262 *arg: _PS.args,
5263 **kw: _PS.kwargs,
5264 ) -> Label[_T]:
5265 sub_element = fn(*arg, **kw)
5266 if sub_element is not self._element:
5267 return Label(self.name, sub_element, type_=self.type)
5268 else:
5269 return self
5270
5271 @property
5272 def primary_key(self): # type: ignore[override]
5273 return self.element.primary_key
5274
5275 @property
5276 def foreign_keys(self): # type: ignore[override]
5277 return self.element.foreign_keys
5278
5279 def _copy_internals(
5280 self,
5281 *,
5282 clone: _CloneCallableType = _clone,
5283 anonymize_labels: bool = False,
5284 **kw: Any,
5285 ) -> None:
5286 self._reset_memoizations()
5287 self._element = clone(self._element, **kw)
5288 if anonymize_labels:
5289 self.name = _anonymous_label.safe_construct(
5290 id(self), getattr(self.element, "name", "anon")
5291 )
5292 self.key = self._tq_label = self._tq_key_label = self.name
5293
5294 @util.ro_non_memoized_property
5295 def _from_objects(self) -> List[FromClause]:
5296 return self.element._from_objects
5297
5298 def _make_proxy(
5299 self,
5300 selectable: FromClause,
5301 *,
5302 primary_key: ColumnSet,
5303 foreign_keys: Set[KeyedColumnElement[Any]],
5304 name: Optional[str] = None,
5305 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5306 **kw: Any,
5307 ) -> typing_Tuple[str, ColumnClause[_T]]:
5308 name = self.name if not name else name
5309
5310 key, e = self.element._make_proxy(
5311 selectable,
5312 name=name,
5313 disallow_is_literal=True,
5314 name_is_truncatable=isinstance(name, _truncated_label),
5315 compound_select_cols=compound_select_cols,
5316 primary_key=primary_key,
5317 foreign_keys=foreign_keys,
5318 )
5319
5320 # there was a note here to remove this assertion, which was here
5321 # to determine if we later could support a use case where
5322 # the key and name of a label are separate. But I don't know what
5323 # that case was. For now, this is an unexpected case that occurs
5324 # when a label name conflicts with other columns and select()
5325 # is attempting to disambiguate an explicit label, which is not what
5326 # the user would want. See issue #6090.
5327 if key != self.name and not isinstance(self.name, _anonymous_label):
5328 raise exc.InvalidRequestError(
5329 "Label name %s is being renamed to an anonymous label due "
5330 "to disambiguation "
5331 "which is not supported right now. Please use unique names "
5332 "for explicit labels." % (self.name)
5333 )
5334
5335 e._propagate_attrs = selectable._propagate_attrs
5336 e._proxies.append(self)
5337 if self.type is not None:
5338 e.type = self.type
5339
5340 return self.key, e
5341
5342
5343class ColumnClause(
5344 roles.DDLReferredColumnRole,
5345 roles.LabeledColumnExprRole[_T],
5346 roles.StrAsPlainColumnRole,
5347 Immutable,
5348 NamedColumn[_T],
5349):
5350 """Represents a column expression from any textual string.
5351
5352 The :class:`.ColumnClause`, a lightweight analogue to the
5353 :class:`_schema.Column` class, is typically invoked using the
5354 :func:`_expression.column` function, as in::
5355
5356 from sqlalchemy import column
5357
5358 id, name = column("id"), column("name")
5359 stmt = select(id, name).select_from("user")
5360
5361 The above statement would produce SQL like:
5362
5363 .. sourcecode:: sql
5364
5365 SELECT id, name FROM user
5366
5367 :class:`.ColumnClause` is the immediate superclass of the schema-specific
5368 :class:`_schema.Column` object. While the :class:`_schema.Column`
5369 class has all the
5370 same capabilities as :class:`.ColumnClause`, the :class:`.ColumnClause`
5371 class is usable by itself in those cases where behavioral requirements
5372 are limited to simple SQL expression generation. The object has none of
5373 the associations with schema-level metadata or with execution-time
5374 behavior that :class:`_schema.Column` does,
5375 so in that sense is a "lightweight"
5376 version of :class:`_schema.Column`.
5377
5378 Full details on :class:`.ColumnClause` usage is at
5379 :func:`_expression.column`.
5380
5381 .. seealso::
5382
5383 :func:`_expression.column`
5384
5385 :class:`_schema.Column`
5386
5387 """
5388
5389 table: Optional[FromClause]
5390 is_literal: bool
5391
5392 __visit_name__ = "column"
5393
5394 _traverse_internals: _TraverseInternalsType = [
5395 ("name", InternalTraversal.dp_anon_name),
5396 ("type", InternalTraversal.dp_type),
5397 ("table", InternalTraversal.dp_clauseelement),
5398 ("is_literal", InternalTraversal.dp_boolean),
5399 ]
5400
5401 onupdate: Optional[DefaultGenerator] = None
5402 default: Optional[DefaultGenerator] = None
5403 server_default: Optional[FetchedValue] = None
5404 server_onupdate: Optional[FetchedValue] = None
5405
5406 _is_multiparam_column = False
5407
5408 @property
5409 def _is_star(self): # type: ignore[override]
5410 return self.is_literal and self.name == "*"
5411
5412 def __init__(
5413 self,
5414 text: str,
5415 type_: Optional[_TypeEngineArgument[_T]] = None,
5416 is_literal: bool = False,
5417 _selectable: Optional[FromClause] = None,
5418 ):
5419 self.key = self.name = text
5420 self.table = _selectable
5421
5422 # if type is None, we get NULLTYPE, which is our _T. But I don't
5423 # know how to get the overloads to express that correctly
5424 self.type = type_api.to_instance(type_) # type: ignore
5425
5426 self.is_literal = is_literal
5427
5428 def get_children(self, *, column_tables=False, **kw):
5429 # override base get_children() to not return the Table
5430 # or selectable that is parent to this column. Traversals
5431 # expect the columns of tables and subqueries to be leaf nodes.
5432 return []
5433
5434 @property
5435 def entity_namespace(self):
5436 if self.table is not None:
5437 return self.table.entity_namespace
5438 else:
5439 return super().entity_namespace
5440
5441 def _clone(self, detect_subquery_cols=False, **kw):
5442 if (
5443 detect_subquery_cols
5444 and self.table is not None
5445 and self.table._is_subquery
5446 ):
5447 clone = kw.pop("clone")
5448 table = clone(self.table, **kw)
5449 new = table.c.corresponding_column(self)
5450 return new
5451
5452 return super()._clone(**kw)
5453
5454 @HasMemoized_ro_memoized_attribute
5455 def _from_objects(self) -> List[FromClause]:
5456 t = self.table
5457 if t is not None:
5458 return [t]
5459 else:
5460 return []
5461
5462 @HasMemoized.memoized_attribute
5463 def _render_label_in_columns_clause(self):
5464 return self.table is not None
5465
5466 @property
5467 def _ddl_label(self):
5468 return self._gen_tq_label(self.name, dedupe_on_key=False)
5469
5470 def _compare_name_for_result(self, other):
5471 if (
5472 self.is_literal
5473 or self.table is None
5474 or self.table._is_textual
5475 or not hasattr(other, "proxy_set")
5476 or (
5477 isinstance(other, ColumnClause)
5478 and (
5479 other.is_literal
5480 or other.table is None
5481 or other.table._is_textual
5482 )
5483 )
5484 ):
5485 return (hasattr(other, "name") and self.name == other.name) or (
5486 hasattr(other, "_tq_label")
5487 and self._tq_label == other._tq_label
5488 )
5489 else:
5490 return other.proxy_set.intersection(self.proxy_set)
5491
5492 def _gen_tq_label(
5493 self, name: str, dedupe_on_key: bool = True
5494 ) -> Optional[str]:
5495 """generate table-qualified label
5496
5497 for a table-bound column this is <tablename>_<columnname>.
5498
5499 used primarily for LABEL_STYLE_TABLENAME_PLUS_COL
5500 as well as the .columns collection on a Join object.
5501
5502 """
5503 label: str
5504 t = self.table
5505 if self.is_literal:
5506 return None
5507 elif t is not None and is_named_from_clause(t):
5508 if has_schema_attr(t) and t.schema:
5509 label = (
5510 t.schema.replace(".", "_") + "_" + t.name + ("_" + name)
5511 )
5512 else:
5513 assert not TYPE_CHECKING or isinstance(t, NamedFromClause)
5514 label = t.name + ("_" + name)
5515
5516 # propagate name quoting rules for labels.
5517 if is_quoted_name(name) and name.quote is not None:
5518 if is_quoted_name(label):
5519 label.quote = name.quote
5520 else:
5521 label = quoted_name(label, name.quote)
5522 elif is_quoted_name(t.name) and t.name.quote is not None:
5523 # can't get this situation to occur, so let's
5524 # assert false on it for now
5525 assert not isinstance(label, quoted_name)
5526 label = quoted_name(label, t.name.quote)
5527
5528 if dedupe_on_key:
5529 # ensure the label name doesn't conflict with that of an
5530 # existing column. note that this implies that any Column
5531 # must **not** set up its _label before its parent table has
5532 # all of its other Column objects set up. There are several
5533 # tables in the test suite which will fail otherwise; example:
5534 # table "owner" has columns "name" and "owner_name". Therefore
5535 # column owner.name cannot use the label "owner_name", it has
5536 # to be "owner_name_1".
5537 if label in t.c:
5538 _label = label
5539 counter = 1
5540 while _label in t.c:
5541 _label = label + f"_{counter}"
5542 counter += 1
5543 label = _label
5544
5545 return coercions.expect(roles.TruncatedLabelRole, label)
5546
5547 else:
5548 return name
5549
5550 def _make_proxy(
5551 self,
5552 selectable: FromClause,
5553 *,
5554 primary_key: ColumnSet,
5555 foreign_keys: Set[KeyedColumnElement[Any]],
5556 name: Optional[str] = None,
5557 key: Optional[str] = None,
5558 name_is_truncatable: bool = False,
5559 compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None,
5560 disallow_is_literal: bool = False,
5561 **kw: Any,
5562 ) -> typing_Tuple[str, ColumnClause[_T]]:
5563 # the "is_literal" flag normally should never be propagated; a proxied
5564 # column is always a SQL identifier and never the actual expression
5565 # being evaluated. however, there is a case where the "is_literal" flag
5566 # might be used to allow the given identifier to have a fixed quoting
5567 # pattern already, so maintain the flag for the proxy unless a
5568 # :class:`.Label` object is creating the proxy. See [ticket:4730].
5569 is_literal = (
5570 not disallow_is_literal
5571 and self.is_literal
5572 and (
5573 # note this does not accommodate for quoted_name differences
5574 # right now
5575 name is None
5576 or name == self.name
5577 )
5578 )
5579 c = self._constructor(
5580 (
5581 coercions.expect(roles.TruncatedLabelRole, name or self.name)
5582 if name_is_truncatable
5583 else (name or self.name)
5584 ),
5585 type_=self.type,
5586 _selectable=selectable,
5587 is_literal=is_literal,
5588 )
5589 c._propagate_attrs = selectable._propagate_attrs
5590 if name is None:
5591 c.key = self.key
5592 if compound_select_cols:
5593 c._proxies = list(compound_select_cols)
5594 else:
5595 c._proxies = [self]
5596
5597 if selectable._is_clone_of is not None:
5598 c._is_clone_of = selectable._is_clone_of.columns.get(c.key)
5599 return c.key, c
5600
5601
5602class TableValuedColumn(NamedColumn[_T]):
5603 __visit_name__ = "table_valued_column"
5604
5605 _traverse_internals: _TraverseInternalsType = [
5606 ("name", InternalTraversal.dp_anon_name),
5607 ("type", InternalTraversal.dp_type),
5608 ("scalar_alias", InternalTraversal.dp_clauseelement),
5609 ]
5610
5611 def __init__(self, scalar_alias: NamedFromClause, type_: TypeEngine[_T]):
5612 self.scalar_alias = scalar_alias
5613 self.key = self.name = scalar_alias.name
5614 self.type = type_
5615
5616 def _copy_internals(
5617 self, clone: _CloneCallableType = _clone, **kw: Any
5618 ) -> None:
5619 self.scalar_alias = clone(self.scalar_alias, **kw)
5620 self.key = self.name = self.scalar_alias.name
5621
5622 @util.ro_non_memoized_property
5623 def _from_objects(self) -> List[FromClause]:
5624 return [self.scalar_alias]
5625
5626
5627class CollationClause(ColumnElement[str]):
5628 __visit_name__ = "collation"
5629
5630 _traverse_internals: _TraverseInternalsType = [
5631 ("collation", InternalTraversal.dp_string)
5632 ]
5633
5634 @classmethod
5635 @util.preload_module("sqlalchemy.sql.sqltypes")
5636 def _create_collation_expression(
5637 cls, expression: _ColumnExpressionArgument[str], collation: str
5638 ) -> BinaryExpression[str]:
5639
5640 sqltypes = util.preloaded.sql_sqltypes
5641
5642 expr = coercions.expect(roles.ExpressionElementRole[str], expression)
5643
5644 if expr.type._type_affinity is sqltypes.String:
5645 collate_type = expr.type._with_collation(collation)
5646 else:
5647 collate_type = expr.type
5648
5649 return BinaryExpression(
5650 expr,
5651 CollationClause(collation),
5652 operators.collate,
5653 type_=collate_type,
5654 )
5655
5656 def __init__(self, collation):
5657 self.collation = collation
5658
5659
5660class _IdentifiedClause(Executable, ClauseElement):
5661 __visit_name__ = "identified"
5662
5663 def __init__(self, ident):
5664 self.ident = ident
5665
5666
5667class SavepointClause(_IdentifiedClause):
5668 __visit_name__ = "savepoint"
5669 inherit_cache = False
5670
5671
5672class RollbackToSavepointClause(_IdentifiedClause):
5673 __visit_name__ = "rollback_to_savepoint"
5674 inherit_cache = False
5675
5676
5677class ReleaseSavepointClause(_IdentifiedClause):
5678 __visit_name__ = "release_savepoint"
5679 inherit_cache = False
5680
5681
5682class quoted_name(util.MemoizedSlots, str):
5683 """Represent a SQL identifier combined with quoting preferences.
5684
5685 :class:`.quoted_name` is a Python unicode/str subclass which
5686 represents a particular identifier name along with a
5687 ``quote`` flag. This ``quote`` flag, when set to
5688 ``True`` or ``False``, overrides automatic quoting behavior
5689 for this identifier in order to either unconditionally quote
5690 or to not quote the name. If left at its default of ``None``,
5691 quoting behavior is applied to the identifier on a per-backend basis
5692 based on an examination of the token itself.
5693
5694 A :class:`.quoted_name` object with ``quote=True`` is also
5695 prevented from being modified in the case of a so-called
5696 "name normalize" option. Certain database backends, such as
5697 Oracle Database, Firebird, and DB2 "normalize" case-insensitive names
5698 as uppercase. The SQLAlchemy dialects for these backends
5699 convert from SQLAlchemy's lower-case-means-insensitive convention
5700 to the upper-case-means-insensitive conventions of those backends.
5701 The ``quote=True`` flag here will prevent this conversion from occurring
5702 to support an identifier that's quoted as all lower case against
5703 such a backend.
5704
5705 The :class:`.quoted_name` object is normally created automatically
5706 when specifying the name for key schema constructs such as
5707 :class:`_schema.Table`, :class:`_schema.Column`, and others.
5708 The class can also be
5709 passed explicitly as the name to any function that receives a name which
5710 can be quoted. Such as to use the :meth:`_engine.Engine.has_table`
5711 method with
5712 an unconditionally quoted name::
5713
5714 from sqlalchemy import create_engine
5715 from sqlalchemy import inspect
5716 from sqlalchemy.sql import quoted_name
5717
5718 engine = create_engine("oracle+oracledb://some_dsn")
5719 print(inspect(engine).has_table(quoted_name("some_table", True)))
5720
5721 The above logic will run the "has table" logic against the Oracle Database
5722 backend, passing the name exactly as ``"some_table"`` without converting to
5723 upper case.
5724
5725 """
5726
5727 __slots__ = "quote", "lower", "upper"
5728
5729 quote: Optional[bool]
5730
5731 @overload
5732 @classmethod
5733 def construct(cls, value: str, quote: Optional[bool]) -> quoted_name: ...
5734
5735 @overload
5736 @classmethod
5737 def construct(cls, value: None, quote: Optional[bool]) -> None: ...
5738
5739 @classmethod
5740 def construct(
5741 cls, value: Optional[str], quote: Optional[bool]
5742 ) -> Optional[quoted_name]:
5743 if value is None:
5744 return None
5745 else:
5746 return quoted_name(value, quote)
5747
5748 def __new__(cls, value: str, quote: Optional[bool]) -> quoted_name:
5749 assert (
5750 value is not None
5751 ), "use quoted_name.construct() for None passthrough"
5752 if isinstance(value, cls) and (quote is None or value.quote == quote):
5753 return value
5754 self = super().__new__(cls, value)
5755
5756 self.quote = quote
5757 return self
5758
5759 def __reduce__(self):
5760 return quoted_name, (str(self), self.quote)
5761
5762 def _memoized_method_lower(self):
5763 if self.quote:
5764 return self
5765 else:
5766 return str(self).lower()
5767
5768 def _memoized_method_upper(self):
5769 if self.quote:
5770 return self
5771 else:
5772 return str(self).upper()
5773
5774
5775def _find_columns(clause: ClauseElement) -> Set[ColumnClause[Any]]:
5776 """locate Column objects within the given expression."""
5777
5778 cols: Set[ColumnClause[Any]] = set()
5779 traverse(clause, {}, {"column": cols.add})
5780 return cols
5781
5782
5783def _type_from_args(args: Sequence[ColumnElement[_T]]) -> TypeEngine[_T]:
5784 for a in args:
5785 if not a.type._isnull:
5786 return a.type
5787 else:
5788 return type_api.NULLTYPE # type: ignore
5789
5790
5791def _corresponding_column_or_error(fromclause, column, require_embedded=False):
5792 c = fromclause.corresponding_column(
5793 column, require_embedded=require_embedded
5794 )
5795 if c is None:
5796 raise exc.InvalidRequestError(
5797 "Given column '%s', attached to table '%s', "
5798 "failed to locate a corresponding column from table '%s'"
5799 % (column, getattr(column, "table", None), fromclause.description)
5800 )
5801 return c
5802
5803
5804class _memoized_property_but_not_nulltype(
5805 util.memoized_property["TypeEngine[_T]"]
5806):
5807 """memoized property, but dont memoize NullType"""
5808
5809 def __get__(self, obj, cls):
5810 if obj is None:
5811 return self
5812 result = self.fget(obj)
5813 if not result._isnull:
5814 obj.__dict__[self.__name__] = result
5815 return result
5816
5817
5818class AnnotatedColumnElement(Annotated):
5819 _Annotated__element: ColumnElement[Any]
5820
5821 def __init__(self, element, values):
5822 Annotated.__init__(self, element, values)
5823 for attr in (
5824 "comparator",
5825 "_proxy_key",
5826 "_tq_key_label",
5827 "_tq_label",
5828 "_non_anon_label",
5829 "type",
5830 ):
5831 self.__dict__.pop(attr, None)
5832 for attr in ("name", "key", "table"):
5833 if self.__dict__.get(attr, False) is None:
5834 self.__dict__.pop(attr)
5835
5836 def _with_annotations(self, values):
5837 clone = super()._with_annotations(values)
5838 for attr in (
5839 "comparator",
5840 "_proxy_key",
5841 "_tq_key_label",
5842 "_tq_label",
5843 "_non_anon_label",
5844 ):
5845 clone.__dict__.pop(attr, None)
5846 return clone
5847
5848 @util.memoized_property
5849 def name(self):
5850 """pull 'name' from parent, if not present"""
5851 return self._Annotated__element.name
5852
5853 @_memoized_property_but_not_nulltype
5854 def type(self):
5855 """pull 'type' from parent and don't cache if null.
5856
5857 type is routinely changed on existing columns within the
5858 mapped_column() initialization process, and "type" is also consulted
5859 during the creation of SQL expressions. Therefore it can change after
5860 it was already retrieved. At the same time we don't want annotated
5861 objects having overhead when expressions are produced, so continue
5862 to memoize, but only when we have a non-null type.
5863
5864 """
5865 return self._Annotated__element.type
5866
5867 @util.memoized_property
5868 def table(self):
5869 """pull 'table' from parent, if not present"""
5870 return self._Annotated__element.table
5871
5872 @util.memoized_property
5873 def key(self):
5874 """pull 'key' from parent, if not present"""
5875 return self._Annotated__element.key
5876
5877 @util.memoized_property
5878 def info(self) -> _InfoType:
5879 if TYPE_CHECKING:
5880 assert isinstance(self._Annotated__element, Column)
5881 return self._Annotated__element.info
5882
5883 @util.memoized_property
5884 def _anon_name_label(self) -> str:
5885 return self._Annotated__element._anon_name_label
5886
5887
5888class _truncated_label(quoted_name):
5889 """A unicode subclass used to identify symbolic "
5890 "names that may require truncation."""
5891
5892 __slots__ = ()
5893
5894 def __new__(cls, value: str, quote: Optional[bool] = None) -> Any:
5895 quote = getattr(value, "quote", quote)
5896 # return super(_truncated_label, cls).__new__(cls, value, quote, True)
5897 return super().__new__(cls, value, quote)
5898
5899 def __reduce__(self) -> Any:
5900 return self.__class__, (str(self), self.quote)
5901
5902 def apply_map(self, map_: Mapping[str, Any]) -> str:
5903 return self
5904
5905
5906class conv(_truncated_label):
5907 """Mark a string indicating that a name has already been converted
5908 by a naming convention.
5909
5910 This is a string subclass that indicates a name that should not be
5911 subject to any further naming conventions.
5912
5913 E.g. when we create a :class:`.Constraint` using a naming convention
5914 as follows::
5915
5916 m = MetaData(
5917 naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}
5918 )
5919 t = Table(
5920 "t", m, Column("x", Integer), CheckConstraint("x > 5", name="x5")
5921 )
5922
5923 The name of the above constraint will be rendered as ``"ck_t_x5"``.
5924 That is, the existing name ``x5`` is used in the naming convention as the
5925 ``constraint_name`` token.
5926
5927 In some situations, such as in migration scripts, we may be rendering
5928 the above :class:`.CheckConstraint` with a name that's already been
5929 converted. In order to make sure the name isn't double-modified, the
5930 new name is applied using the :func:`_schema.conv` marker. We can
5931 use this explicitly as follows::
5932
5933
5934 m = MetaData(
5935 naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}
5936 )
5937 t = Table(
5938 "t",
5939 m,
5940 Column("x", Integer),
5941 CheckConstraint("x > 5", name=conv("ck_t_x5")),
5942 )
5943
5944 Where above, the :func:`_schema.conv` marker indicates that the constraint
5945 name here is final, and the name will render as ``"ck_t_x5"`` and not
5946 ``"ck_t_ck_t_x5"``
5947
5948 .. seealso::
5949
5950 :ref:`constraint_naming_conventions`
5951
5952 """
5953
5954 __slots__ = ()
5955
5956
5957# for backwards compatibility in case
5958# someone is re-implementing the
5959# _truncated_identifier() sequence in a custom
5960# compiler
5961_generated_label = _truncated_label
5962_anonymous_label_escape = re.compile(r"[%\(\) \$]+")
5963
5964
5965class _anonymous_label(_truncated_label):
5966 """A unicode subclass used to identify anonymously
5967 generated names."""
5968
5969 __slots__ = ()
5970
5971 @classmethod
5972 def safe_construct_with_key(
5973 cls, seed: int | str, body: str, sanitize_key: bool = False
5974 ) -> typing_Tuple[_anonymous_label, str]:
5975 # need to escape chars that interfere with format
5976 # strings in any case, issue #8724
5977 body = _anonymous_label_escape.sub("_", body)
5978
5979 if sanitize_key:
5980 # sanitize_key is then an extra step used by BindParameter
5981 body = body.strip("_")
5982
5983 key = f"{seed} {body.replace('%', '%%')}"
5984 label = _anonymous_label(f"%({key})s")
5985 return label, key
5986
5987 @classmethod
5988 def safe_construct(
5989 cls, seed: int | str, body: str, sanitize_key: bool = False
5990 ) -> _anonymous_label:
5991 # need to escape chars that interfere with format
5992 # strings in any case, issue #8724
5993 body = _anonymous_label_escape.sub("_", body)
5994
5995 if sanitize_key:
5996 # sanitize_key is then an extra step used by BindParameter
5997 body = body.strip("_")
5998
5999 return _anonymous_label(f"%({seed} {body.replace('%', '%%')})s")
6000
6001 def __add__(self, other: str) -> _anonymous_label:
6002 if "%" in other and not isinstance(other, _anonymous_label):
6003 other = str(other).replace("%", "%%")
6004 else:
6005 other = str(other)
6006
6007 return _anonymous_label(
6008 quoted_name(
6009 str.__add__(self, other),
6010 self.quote,
6011 )
6012 )
6013
6014 def __radd__(self, other: str) -> _anonymous_label:
6015 if "%" in other and not isinstance(other, _anonymous_label):
6016 other = str(other).replace("%", "%%")
6017 else:
6018 other = str(other)
6019
6020 return _anonymous_label(
6021 quoted_name(
6022 str.__add__(other, self),
6023 self.quote,
6024 )
6025 )
6026
6027 def apply_map(self, map_: Mapping[str, Any]) -> str:
6028 if self.quote is not None:
6029 # preserve quoting only if necessary
6030 return quoted_name(self % map_, self.quote)
6031 else:
6032 # else skip the constructor call
6033 return self % map_