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