1# sql/selectable.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
8"""The :class:`_expression.FromClause` class of SQL expression elements,
9representing
10SQL tables and derived rowsets.
11
12"""
13
14from __future__ import annotations
15
16import collections
17from enum import Enum
18import itertools
19import os
20from typing import AbstractSet
21from typing import Any as TODO_Any
22from typing import Any
23from typing import Callable
24from typing import cast
25from typing import Collection
26from typing import Dict
27from typing import Generic
28from typing import Iterable
29from typing import Iterator
30from typing import List
31from typing import Literal
32from typing import NamedTuple
33from typing import NoReturn
34from typing import Optional
35from typing import overload
36from typing import Protocol
37from typing import Sequence
38from typing import Set
39from typing import Tuple
40from typing import Type
41from typing import TYPE_CHECKING
42from typing import TypeVar
43from typing import Union
44
45from . import cache_key
46from . import coercions
47from . import operators
48from . import roles
49from . import traversals
50from . import type_api
51from . import visitors
52from ._annotated_cols import _ColClauseCC_co
53from ._annotated_cols import _KeyColCC_co
54from ._annotated_cols import _TC_co
55from ._annotated_cols import HasRowPos
56from ._typing import _ColumnsClauseArgument
57from ._typing import _no_kw
58from ._typing import _T
59from ._typing import _Ts
60from ._typing import is_column_element
61from ._typing import is_select_statement
62from ._typing import is_subquery
63from ._typing import is_table
64from ._typing import is_text_clause
65from .annotation import Annotated
66from .annotation import SupportsCloneAnnotations
67from .base import _clone
68from .base import _cloned_difference
69from .base import _cloned_intersection
70from .base import _entity_namespace_key_search_all
71from .base import _EntityNamespace
72from .base import _expand_cloned
73from .base import _from_objects
74from .base import _generative
75from .base import _never_select_column
76from .base import _NoArg
77from .base import _select_iterables
78from .base import CacheableOptions
79from .base import ColumnCollection
80from .base import ColumnSet
81from .base import CompileState
82from .base import DedupeColumnCollection
83from .base import DialectKWArgs
84from .base import Executable
85from .base import ExecutableStatement
86from .base import Generative
87from .base import HasCompileState
88from .base import HasMemoized
89from .base import HasSyntaxExtensions
90from .base import Immutable
91from .base import SyntaxExtension
92from .base import WriteableColumnCollection
93from .coercions import _document_text_coercion
94from .elements import _anonymous_label
95from .elements import BindParameter
96from .elements import BooleanClauseList
97from .elements import ClauseElement
98from .elements import ClauseList
99from .elements import ColumnClause
100from .elements import ColumnElement
101from .elements import DQLDMLClauseElement
102from .elements import GroupedElement
103from .elements import literal_column
104from .elements import TableValuedColumn
105from .elements import TextClause
106from .elements import UnaryExpression
107from .operators import OperatorType
108from .sqltypes import NULLTYPE
109from .visitors import _TraverseInternalsType
110from .visitors import InternalTraversal
111from .visitors import prefix_anon_map
112from .. import exc
113from .. import util
114from ..util import HasMemoized_ro_memoized_attribute
115from ..util import warn_deprecated
116from ..util.typing import Self
117from ..util.typing import TupleAny
118from ..util.typing import Unpack
119
120and_ = BooleanClauseList.and_
121
122
123if TYPE_CHECKING:
124 from ._typing import _ColumnExpressionArgument
125 from ._typing import _ColumnExpressionOrStrLabelArgument
126 from ._typing import _FromClauseArgument
127 from ._typing import _JoinTargetArgument
128 from ._typing import _LimitOffsetType
129 from ._typing import _MAYBE_ENTITY
130 from ._typing import _NOT_ENTITY
131 from ._typing import _OnClauseArgument
132 from ._typing import _OnlyColumnArgument
133 from ._typing import _SelectStatementForCompoundArgument
134 from ._typing import _T0
135 from ._typing import _T1
136 from ._typing import _T2
137 from ._typing import _T3
138 from ._typing import _T4
139 from ._typing import _T5
140 from ._typing import _T6
141 from ._typing import _T7
142 from ._typing import _TextCoercedExpressionArgument
143 from ._typing import _TypedColumnClauseArgument as _TCCA
144 from ._typing import _TypeEngineArgument
145 from .base import _AmbiguousTableNameMap
146 from .base import ExecutableOption
147 from .base import ReadOnlyColumnCollection
148 from .cache_key import _CacheKeyTraversalType
149 from .compiler import SQLCompiler
150 from .ddl import CreateTableAs
151 from .dml import Delete
152 from .dml import Update
153 from .elements import AbstractTextClause
154 from .elements import BinaryExpression
155 from .elements import KeyedColumnElement
156 from .elements import Label
157 from .elements import NamedColumn
158 from .functions import Function
159 from .schema import ForeignKey
160 from .schema import ForeignKeyConstraint
161 from .schema import MetaData
162 from .sqltypes import TableValueType
163 from .type_api import TypeEngine
164 from .visitors import _CloneCallableType
165
166
167_ColumnsClauseElement = Union["FromClause", ColumnElement[Any], "TextClause"]
168_LabelConventionCallable = Callable[
169 [Union["ColumnElement[Any]", "AbstractTextClause"]], Optional[str]
170]
171
172
173class _JoinTargetProtocol(Protocol):
174 @util.ro_non_memoized_property
175 def _from_objects(self) -> List[FromClause]: ...
176
177 @util.ro_non_memoized_property
178 def entity_namespace(self) -> _EntityNamespace: ...
179
180
181_JoinTargetElement = Union["FromClause", _JoinTargetProtocol]
182_OnClauseElement = Union["ColumnElement[bool]", _JoinTargetProtocol]
183
184_ForUpdateOfArgument = Union[
185 # single column, Table, ORM entity
186 Union[
187 "_ColumnExpressionArgument[Any]",
188 "_FromClauseArgument",
189 ],
190 # or sequence of column, Table, ORM entity
191 Sequence[
192 Union[
193 "_ColumnExpressionArgument[Any]",
194 "_FromClauseArgument",
195 ]
196 ],
197]
198
199
200_SetupJoinsElement = Tuple[
201 _JoinTargetElement,
202 Optional[_OnClauseElement],
203 Optional["FromClause"],
204 Dict[str, Any],
205]
206
207
208_SelectIterable = Iterable[Union["ColumnElement[Any]", "AbstractTextClause"]]
209
210
211class _OffsetLimitParam(BindParameter[int]):
212 inherit_cache = True
213
214 @property
215 def _limit_offset_value(self) -> Optional[int]:
216 return self.effective_value
217
218
219class ReturnsRows(roles.ReturnsRowsRole, DQLDMLClauseElement):
220 """The base-most class for Core constructs that have some concept of
221 columns that can represent rows.
222
223 While the SELECT statement and TABLE are the primary things we think
224 of in this category, DML like INSERT, UPDATE and DELETE can also specify
225 RETURNING which means they can be used in CTEs and other forms, and
226 PostgreSQL has functions that return rows also.
227
228 .. versionadded:: 1.4
229
230 """
231
232 _is_returns_rows = True
233
234 # sub-elements of returns_rows
235 _is_from_clause = False
236 _is_select_base = False
237 _is_select_statement = False
238 _is_lateral = False
239
240 @property
241 def selectable(self) -> ReturnsRows:
242 return self
243
244 @util.ro_non_memoized_property
245 def _all_selected_columns(self) -> _SelectIterable:
246 """A sequence of column expression objects that represents the
247 "selected" columns of this :class:`_expression.ReturnsRows`.
248
249 This is typically equivalent to .exported_columns except it is
250 delivered in the form of a straight sequence and not keyed
251 :class:`_expression.ColumnCollection`.
252
253 """
254 raise NotImplementedError()
255
256 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
257 """Return ``True`` if this :class:`.ReturnsRows` is
258 'derived' from the given :class:`.FromClause`.
259
260 An example would be an Alias of a Table is derived from that Table.
261
262 """
263 raise NotImplementedError()
264
265 def _generate_fromclause_column_proxies(
266 self,
267 fromclause: FromClause,
268 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
269 primary_key: ColumnSet,
270 foreign_keys: Set[KeyedColumnElement[Any]],
271 ) -> None:
272 """Populate columns into an :class:`.AliasedReturnsRows` object."""
273
274 raise NotImplementedError()
275
276 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
277 """reset internal collections for an incoming column being added."""
278 raise NotImplementedError()
279
280 @property
281 def exported_columns(self) -> ReadOnlyColumnCollection[Any, Any]:
282 """A :class:`_expression.ColumnCollection`
283 that represents the "exported"
284 columns of this :class:`_expression.ReturnsRows`.
285
286 The "exported" columns represent the collection of
287 :class:`_expression.ColumnElement`
288 expressions that are rendered by this SQL
289 construct. There are primary varieties which are the
290 "FROM clause columns" of a FROM clause, such as a table, join,
291 or subquery, the "SELECTed columns", which are the columns in
292 the "columns clause" of a SELECT statement, and the RETURNING
293 columns in a DML statement..
294
295 .. versionadded:: 1.4
296
297 .. seealso::
298
299 :attr:`_expression.FromClause.exported_columns`
300
301 :attr:`_expression.SelectBase.exported_columns`
302 """
303
304 raise NotImplementedError()
305
306
307class ExecutableReturnsRows(ExecutableStatement, ReturnsRows):
308 """base for executable statements that return rows."""
309
310
311class TypedReturnsRows(ExecutableReturnsRows, Generic[Unpack[_Ts]]):
312 """base for a typed executable statements that return rows."""
313
314
315class Selectable(ReturnsRows):
316 """Mark a class as being selectable."""
317
318 __visit_name__ = "selectable"
319
320 is_selectable = True
321
322 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
323 raise NotImplementedError()
324
325 def lateral(self, name: Optional[str] = None) -> LateralFromClause:
326 """Return a LATERAL alias of this :class:`_expression.Selectable`.
327
328 The return value is the :class:`_expression.Lateral` construct also
329 provided by the top-level :func:`_expression.lateral` function.
330
331 .. seealso::
332
333 :ref:`tutorial_lateral_correlation` - overview of usage.
334
335 """
336 return Lateral._construct(self, name=name)
337
338 @util.deprecated(
339 "1.4",
340 message="The :meth:`.Selectable.replace_selectable` method is "
341 "deprecated, and will be removed in a future release. Similar "
342 "functionality is available via the sqlalchemy.sql.visitors module.",
343 )
344 @util.preload_module("sqlalchemy.sql.util")
345 def replace_selectable(self, old: FromClause, alias: Alias) -> Self:
346 """Replace all occurrences of :class:`_expression.FromClause`
347 'old' with the given :class:`_expression.Alias`
348 object, returning a copy of this :class:`_expression.FromClause`.
349
350 """
351 return util.preloaded.sql_util.ClauseAdapter(alias).traverse(self)
352
353 def corresponding_column(
354 self, column: KeyedColumnElement[Any], require_embedded: bool = False
355 ) -> Optional[KeyedColumnElement[Any]]:
356 """Given a :class:`_expression.ColumnElement`, return the exported
357 :class:`_expression.ColumnElement` object from the
358 :attr:`_expression.Selectable.exported_columns`
359 collection of this :class:`_expression.Selectable`
360 which corresponds to that
361 original :class:`_expression.ColumnElement` via a common ancestor
362 column.
363
364 :param column: the target :class:`_expression.ColumnElement`
365 to be matched.
366
367 :param require_embedded: only return corresponding columns for
368 the given :class:`_expression.ColumnElement`, if the given
369 :class:`_expression.ColumnElement`
370 is actually present within a sub-element
371 of this :class:`_expression.Selectable`.
372 Normally the column will match if
373 it merely shares a common ancestor with one of the exported
374 columns of this :class:`_expression.Selectable`.
375
376 .. seealso::
377
378 :attr:`_expression.Selectable.exported_columns` - the
379 :class:`_expression.ColumnCollection`
380 that is used for the operation.
381
382 :meth:`_expression.ColumnCollection.corresponding_column`
383 - implementation
384 method.
385
386 """
387
388 return self.exported_columns.corresponding_column(
389 column, require_embedded
390 )
391
392
393class HasPrefixes:
394 _prefixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = ()
395
396 _has_prefixes_traverse_internals: _TraverseInternalsType = [
397 ("_prefixes", InternalTraversal.dp_prefix_sequence)
398 ]
399
400 @_generative
401 @_document_text_coercion(
402 "prefixes",
403 ":meth:`_expression.HasPrefixes.prefix_with`",
404 ":paramref:`.HasPrefixes.prefix_with.*prefixes`",
405 )
406 def prefix_with(
407 self,
408 *prefixes: _TextCoercedExpressionArgument[Any],
409 dialect: str = "*",
410 ) -> Self:
411 r"""Add one or more expressions following the statement keyword, i.e.
412 SELECT, INSERT, UPDATE, or DELETE. Generative.
413
414 This is used to support backend-specific prefix keywords such as those
415 provided by MySQL.
416
417 E.g.::
418
419 stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql")
420
421 # MySQL 5.7 optimizer hints
422 stmt = select(table).prefix_with("/*+ BKA(t1) */", dialect="mysql")
423
424 Multiple prefixes can be specified by multiple calls
425 to :meth:`_expression.HasPrefixes.prefix_with`.
426
427 :param \*prefixes: textual or :class:`_expression.ClauseElement`
428 construct which
429 will be rendered following the INSERT, UPDATE, or DELETE
430 keyword.
431 :param dialect: optional string dialect name which will
432 limit rendering of this prefix to only that dialect.
433
434 """
435 self._prefixes = self._prefixes + tuple(
436 [
437 (coercions.expect(roles.StatementOptionRole, p), dialect)
438 for p in prefixes
439 ]
440 )
441 return self
442
443
444class HasSuffixes:
445 _suffixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = ()
446
447 _has_suffixes_traverse_internals: _TraverseInternalsType = [
448 ("_suffixes", InternalTraversal.dp_prefix_sequence)
449 ]
450
451 @_generative
452 @_document_text_coercion(
453 "suffixes",
454 ":meth:`_expression.HasSuffixes.suffix_with`",
455 ":paramref:`.HasSuffixes.suffix_with.*suffixes`",
456 )
457 def suffix_with(
458 self,
459 *suffixes: _TextCoercedExpressionArgument[Any],
460 dialect: str = "*",
461 ) -> Self:
462 r"""Add one or more expressions following the statement as a whole.
463
464 This is used to support backend-specific suffix keywords on
465 certain constructs.
466
467 E.g.::
468
469 stmt = (
470 select(col1, col2)
471 .cte()
472 .suffix_with(
473 "cycle empno set y_cycle to 1 default 0", dialect="oracle"
474 )
475 )
476
477 Multiple suffixes can be specified by multiple calls
478 to :meth:`_expression.HasSuffixes.suffix_with`.
479
480 :param \*suffixes: textual or :class:`_expression.ClauseElement`
481 construct which
482 will be rendered following the target clause.
483 :param dialect: Optional string dialect name which will
484 limit rendering of this suffix to only that dialect.
485
486 """
487 self._suffixes = self._suffixes + tuple(
488 [
489 (coercions.expect(roles.StatementOptionRole, p), dialect)
490 for p in suffixes
491 ]
492 )
493 return self
494
495
496class HasHints:
497 _hints: util.immutabledict[Tuple[FromClause, str], str] = (
498 util.immutabledict()
499 )
500 _statement_hints: Tuple[Tuple[str, str], ...] = ()
501
502 _has_hints_traverse_internals: _TraverseInternalsType = [
503 ("_statement_hints", InternalTraversal.dp_statement_hint_list),
504 ("_hints", InternalTraversal.dp_table_hint_list),
505 ]
506
507 @_generative
508 def with_statement_hint(self, text: str, dialect_name: str = "*") -> Self:
509 """Add a statement hint to this :class:`_expression.Select` or
510 other selectable object.
511
512 .. tip::
513
514 :meth:`_expression.Select.with_statement_hint` generally adds hints
515 **at the trailing end** of a SELECT statement. To place
516 dialect-specific hints such as optimizer hints at the **front** of
517 the SELECT statement after the SELECT keyword, use the
518 :meth:`_expression.Select.prefix_with` method for an open-ended
519 space, or for table-specific hints the
520 :meth:`_expression.Select.with_hint` may be used, which places
521 hints in a dialect-specific location.
522
523 This method is similar to :meth:`_expression.Select.with_hint` except
524 that it does not require an individual table, and instead applies to
525 the statement as a whole.
526
527 Hints here are specific to the backend database and may include
528 directives such as isolation levels, file directives, fetch directives,
529 etc.
530
531 .. seealso::
532
533 :meth:`_expression.Select.with_hint`
534
535 :meth:`_expression.Select.prefix_with` - generic SELECT prefixing
536 which also can suit some database-specific HINT syntaxes such as
537 MySQL or Oracle Database optimizer hints
538
539 """
540 return self._with_hint(None, text, dialect_name)
541
542 @_generative
543 def with_hint(
544 self,
545 selectable: _FromClauseArgument,
546 text: str,
547 dialect_name: str = "*",
548 ) -> Self:
549 r"""Add an indexing or other executional context hint for the given
550 selectable to this :class:`_expression.Select` or other selectable
551 object.
552
553 .. tip::
554
555 The :meth:`_expression.Select.with_hint` method adds hints that are
556 **specific to a single table** to a statement, in a location that
557 is **dialect-specific**. To add generic optimizer hints to the
558 **beginning** of a statement ahead of the SELECT keyword such as
559 for MySQL or Oracle Database, use the
560 :meth:`_expression.Select.prefix_with` method. To add optimizer
561 hints to the **end** of a statement such as for PostgreSQL, use the
562 :meth:`_expression.Select.with_statement_hint` method.
563
564 The text of the hint is rendered in the appropriate
565 location for the database backend in use, relative
566 to the given :class:`_schema.Table` or :class:`_expression.Alias`
567 passed as the
568 ``selectable`` argument. The dialect implementation
569 typically uses Python string substitution syntax
570 with the token ``%(name)s`` to render the name of
571 the table or alias. E.g. when using Oracle Database, the
572 following::
573
574 select(mytable).with_hint(mytable, "index(%(name)s ix_mytable)")
575
576 Would render SQL as:
577
578 .. sourcecode:: sql
579
580 select /*+ index(mytable ix_mytable) */ ... from mytable
581
582 The ``dialect_name`` option will limit the rendering of a particular
583 hint to a particular backend. Such as, to add hints for both Oracle
584 Database and MSSql simultaneously::
585
586 select(mytable).with_hint(
587 mytable, "index(%(name)s ix_mytable)", "oracle"
588 ).with_hint(mytable, "WITH INDEX ix_mytable", "mssql")
589
590 .. seealso::
591
592 :meth:`_expression.Select.with_statement_hint`
593
594 :meth:`_expression.Select.prefix_with` - generic SELECT prefixing
595 which also can suit some database-specific HINT syntaxes such as
596 MySQL or Oracle Database optimizer hints
597
598 """
599
600 return self._with_hint(selectable, text, dialect_name)
601
602 def _with_hint(
603 self,
604 selectable: Optional[_FromClauseArgument],
605 text: str,
606 dialect_name: str,
607 ) -> Self:
608 if selectable is None:
609 self._statement_hints += ((dialect_name, text),)
610 else:
611 self._hints = self._hints.union(
612 {
613 (
614 coercions.expect(roles.FromClauseRole, selectable),
615 dialect_name,
616 ): text
617 }
618 )
619 return self
620
621
622class FromClause(
623 roles.AnonymizedFromClauseRole, Generic[_KeyColCC_co], Selectable
624):
625 """Represent an element that can be used within the ``FROM``
626 clause of a ``SELECT`` statement.
627
628 The most common forms of :class:`_expression.FromClause` are the
629 :class:`_schema.Table` and the :func:`_expression.select` constructs. Key
630 features common to all :class:`_expression.FromClause` objects include:
631
632 * a :attr:`.c` collection, which provides per-name access to a collection
633 of :class:`_expression.ColumnElement` objects.
634 * a :attr:`.primary_key` attribute, which is a collection of all those
635 :class:`_expression.ColumnElement`
636 objects that indicate the ``primary_key`` flag.
637 * Methods to generate various derivations of a "from" clause, including
638 :meth:`_expression.FromClause.alias`,
639 :meth:`_expression.FromClause.join`,
640 :meth:`_expression.FromClause.select`.
641
642
643 """
644
645 __visit_name__ = "fromclause"
646 named_with_column = False
647
648 @util.ro_non_memoized_property
649 def _hide_froms(self) -> Iterable[FromClause]:
650 return ()
651
652 _is_clone_of: Optional[FromClause[_KeyColCC_co]]
653
654 _columns: WriteableColumnCollection[Any, Any]
655
656 schema: Optional[str] = None
657 """Define the 'schema' attribute for this :class:`_expression.FromClause`.
658
659 This is typically ``None`` for most objects except that of
660 :class:`_schema.Table`, where it is taken as the value of the
661 :paramref:`_schema.Table.schema` argument.
662
663 """
664
665 is_selectable = True
666 _is_from_clause = True
667 _is_join = False
668
669 _use_schema_map = False
670
671 def with_cols(self, type_: Type[_TC_co]) -> FromClause[_TC_co]:
672 """Cast this :class:`.FromClause` to be generic on a specific a
673 :class:`_schema.TypedColumns` subclass.
674
675 At runtime returns self unchanged, without performing any validation.
676 """
677 return self # type: ignore[return-value]
678
679 @overload
680 def select(
681 self: FromClause[HasRowPos[Unpack[_Ts]]], # type: ignore[type-var]
682 ) -> Select[Unpack[_Ts]]: ...
683 @overload
684 def select(self) -> Select[Unpack[TupleAny]]: ...
685
686 def select(self) -> Select[Unpack[TupleAny]]:
687 r"""Return a SELECT of this :class:`_expression.FromClause`.
688
689
690 e.g.::
691
692 stmt = some_table.select().where(some_table.c.id == 5)
693
694 .. seealso::
695
696 :func:`_expression.select` - general purpose
697 method which allows for arbitrary column lists.
698
699 """
700 return Select(self)
701
702 def join(
703 self,
704 right: _FromClauseArgument,
705 onclause: Optional[_ColumnExpressionArgument[bool]] = None,
706 isouter: bool = False,
707 full: bool = False,
708 ) -> Join:
709 """Return a :class:`_expression.Join` from this
710 :class:`_expression.FromClause`
711 to another :class:`FromClause`.
712
713 E.g.::
714
715 from sqlalchemy import join
716
717 j = user_table.join(
718 address_table, user_table.c.id == address_table.c.user_id
719 )
720 stmt = select(user_table).select_from(j)
721
722 would emit SQL along the lines of:
723
724 .. sourcecode:: sql
725
726 SELECT user.id, user.name FROM user
727 JOIN address ON user.id = address.user_id
728
729 :param right: the right side of the join; this is any
730 :class:`_expression.FromClause` object such as a
731 :class:`_schema.Table` object, and
732 may also be a selectable-compatible object such as an ORM-mapped
733 class.
734
735 :param onclause: a SQL expression representing the ON clause of the
736 join. If left at ``None``, :meth:`_expression.FromClause.join`
737 will attempt to
738 join the two tables based on a foreign key relationship.
739
740 :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN.
741
742 :param full: if True, render a FULL OUTER JOIN, instead of LEFT OUTER
743 JOIN. Implies :paramref:`.FromClause.join.isouter`.
744
745 .. seealso::
746
747 :func:`_expression.join` - standalone function
748
749 :class:`_expression.Join` - the type of object produced
750
751 """
752
753 return Join(self, right, onclause, isouter, full)
754
755 def outerjoin(
756 self,
757 right: _FromClauseArgument,
758 onclause: Optional[_ColumnExpressionArgument[bool]] = None,
759 full: bool = False,
760 ) -> Join:
761 """Return a :class:`_expression.Join` from this
762 :class:`_expression.FromClause`
763 to another :class:`FromClause`, with the "isouter" flag set to
764 True.
765
766 E.g.::
767
768 from sqlalchemy import outerjoin
769
770 j = user_table.outerjoin(
771 address_table, user_table.c.id == address_table.c.user_id
772 )
773
774 The above is equivalent to::
775
776 j = user_table.join(
777 address_table, user_table.c.id == address_table.c.user_id, isouter=True
778 )
779
780 :param right: the right side of the join; this is any
781 :class:`_expression.FromClause` object such as a
782 :class:`_schema.Table` object, and
783 may also be a selectable-compatible object such as an ORM-mapped
784 class.
785
786 :param onclause: a SQL expression representing the ON clause of the
787 join. If left at ``None``, :meth:`_expression.FromClause.join`
788 will attempt to
789 join the two tables based on a foreign key relationship.
790
791 :param full: if True, render a FULL OUTER JOIN, instead of
792 LEFT OUTER JOIN.
793
794 .. seealso::
795
796 :meth:`_expression.FromClause.join`
797
798 :class:`_expression.Join`
799
800 """ # noqa: E501
801
802 return Join(self, right, onclause, True, full)
803
804 def alias(
805 self, name: Optional[str] = None, flat: bool = False
806 ) -> NamedFromClause[_KeyColCC_co]:
807 """Return an alias of this :class:`_expression.FromClause`.
808
809 E.g.::
810
811 a2 = some_table.alias("a2")
812
813 The above code creates an :class:`_expression.Alias`
814 object which can be used
815 as a FROM clause in any SELECT statement.
816
817 .. seealso::
818
819 :ref:`tutorial_using_aliases`
820
821 :func:`_expression.alias`
822
823 """
824
825 return Alias._construct(self, name=name)
826
827 def tablesample(
828 self,
829 sampling: Union[float, Function[Any]],
830 name: Optional[str] = None,
831 seed: Optional[roles.ExpressionElementRole[Any]] = None,
832 ) -> TableSample:
833 """Return a TABLESAMPLE alias of this :class:`_expression.FromClause`.
834
835 The return value is the :class:`_expression.TableSample`
836 construct also
837 provided by the top-level :func:`_expression.tablesample` function.
838
839 .. seealso::
840
841 :func:`_expression.tablesample` - usage guidelines and parameters
842
843 """
844 return TableSample._construct(
845 self, sampling=sampling, name=name, seed=seed
846 )
847
848 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
849 """Return ``True`` if this :class:`_expression.FromClause` is
850 'derived' from the given ``FromClause``.
851
852 An example would be an Alias of a Table is derived from that Table.
853
854 """
855 # this is essentially an "identity" check in the base class.
856 # Other constructs override this to traverse through
857 # contained elements.
858 return fromclause in self._cloned_set
859
860 def _is_lexical_equivalent(self, other: FromClause) -> bool:
861 """Return ``True`` if this :class:`_expression.FromClause` and
862 the other represent the same lexical identity.
863
864 This tests if either one is a copy of the other, or
865 if they are the same via annotation identity.
866
867 """
868 return bool(self._cloned_set.intersection(other._cloned_set))
869
870 @util.ro_non_memoized_property
871 def description(self) -> str:
872 """A brief description of this :class:`_expression.FromClause`.
873
874 Used primarily for error message formatting.
875
876 """
877 return getattr(self, "name", self.__class__.__name__ + " object")
878
879 def _generate_fromclause_column_proxies(
880 self,
881 fromclause: FromClause,
882 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
883 primary_key: ColumnSet,
884 foreign_keys: Set[KeyedColumnElement[Any]],
885 ) -> None:
886 columns._populate_separate_keys(
887 col._make_proxy(
888 fromclause, primary_key=primary_key, foreign_keys=foreign_keys
889 )
890 for col in self.c
891 )
892
893 @util.ro_non_memoized_property
894 def exported_columns(self) -> _KeyColCC_co:
895 """A :class:`_expression.ColumnCollection`
896 that represents the "exported"
897 columns of this :class:`_expression.FromClause`.
898
899 The "exported" columns for a :class:`_expression.FromClause`
900 object are synonymous
901 with the :attr:`_expression.FromClause.columns` collection.
902
903 .. versionadded:: 1.4
904
905 .. seealso::
906
907 :attr:`_expression.Selectable.exported_columns`
908
909 :attr:`_expression.SelectBase.exported_columns`
910
911
912 """
913 return self.c
914
915 @util.ro_non_memoized_property
916 def columns(self) -> _KeyColCC_co:
917 """A named-based collection of :class:`_expression.ColumnElement`
918 objects maintained by this :class:`_expression.FromClause`.
919
920 The :attr:`.columns`, or :attr:`.c` collection, is the gateway
921 to the construction of SQL expressions using table-bound or
922 other selectable-bound columns::
923
924 select(mytable).where(mytable.c.somecolumn == 5)
925
926 :return: a :class:`.ColumnCollection` object.
927
928 """
929 return self.c
930
931 @util.ro_memoized_property
932 def c(self) -> _KeyColCC_co:
933 """
934 A synonym for :attr:`.FromClause.columns`
935
936 :return: a :class:`.ColumnCollection`
937
938 """
939 if "_columns" not in self.__dict__:
940 self._setup_collections()
941 return self._columns.as_readonly() # type: ignore[return-value]
942
943 def _setup_collections(self) -> None:
944 with util.mini_gil:
945 # detect another thread that raced ahead
946 if "_columns" in self.__dict__:
947 assert "primary_key" in self.__dict__
948 assert "foreign_keys" in self.__dict__
949 return
950
951 _columns: WriteableColumnCollection[Any, Any] = (
952 WriteableColumnCollection()
953 )
954 primary_key = ColumnSet()
955 foreign_keys: Set[KeyedColumnElement[Any]] = set()
956
957 self._populate_column_collection(
958 columns=_columns,
959 primary_key=primary_key,
960 foreign_keys=foreign_keys,
961 )
962
963 # assigning these three collections separately is not itself
964 # atomic, but greatly reduces the surface for problems
965 self._columns = _columns
966 self.primary_key = primary_key # type: ignore[misc]
967 self.foreign_keys = foreign_keys # type: ignore[assignment, misc]
968
969 @util.ro_non_memoized_property
970 def entity_namespace(self) -> _EntityNamespace:
971 """Return a namespace used for name-based access in SQL expressions.
972
973 This is the namespace that is used to resolve "filter_by()" type
974 expressions, such as::
975
976 stmt.filter_by(address="some address")
977
978 It defaults to the ``.c`` collection, however internally it can
979 be overridden using the "entity_namespace" annotation to deliver
980 alternative results.
981
982 """
983 return self.c
984
985 @util.ro_memoized_property
986 def primary_key(self) -> Iterable[NamedColumn[Any]]:
987 """Return the iterable collection of :class:`_schema.Column` objects
988 which comprise the primary key of this :class:`_selectable.FromClause`.
989
990 For a :class:`_schema.Table` object, this collection is represented
991 by the :class:`_schema.PrimaryKeyConstraint` which itself is an
992 iterable collection of :class:`_schema.Column` objects.
993
994 """
995 self._setup_collections()
996 return self.primary_key
997
998 @util.ro_memoized_property
999 def foreign_keys(self) -> Iterable[ForeignKey]:
1000 """Return the collection of :class:`_schema.ForeignKey` marker objects
1001 which this FromClause references.
1002
1003 Each :class:`_schema.ForeignKey` is a member of a
1004 :class:`_schema.Table`-wide
1005 :class:`_schema.ForeignKeyConstraint`.
1006
1007 .. seealso::
1008
1009 :attr:`_schema.Table.foreign_key_constraints`
1010
1011 """
1012 self._setup_collections()
1013 return self.foreign_keys
1014
1015 def _reset_column_collection(self) -> None:
1016 """Reset the attributes linked to the ``FromClause.c`` attribute.
1017
1018 This collection is separate from all the other memoized things
1019 as it has shown to be sensitive to being cleared out in situations
1020 where enclosing code, typically in a replacement traversal scenario,
1021 has already established strong relationships
1022 with the exported columns.
1023
1024 The collection is cleared for the case where a table is having a
1025 column added to it as well as within a Join during copy internals.
1026
1027 """
1028
1029 for key in ["_columns", "columns", "c", "primary_key", "foreign_keys"]:
1030 self.__dict__.pop(key, None)
1031
1032 @util.ro_non_memoized_property
1033 def _select_iterable(self) -> _SelectIterable:
1034 return (c for c in self.c if not _never_select_column(c))
1035
1036 @property
1037 def _cols_populated(self) -> bool:
1038 return "_columns" in self.__dict__
1039
1040 def _populate_column_collection(
1041 self,
1042 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
1043 primary_key: ColumnSet,
1044 foreign_keys: Set[KeyedColumnElement[Any]],
1045 ) -> None:
1046 """Called on subclasses to establish the .c collection.
1047
1048 Each implementation has a different way of establishing
1049 this collection.
1050
1051 """
1052
1053 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
1054 """Given a column added to the .c collection of an underlying
1055 selectable, produce the local version of that column, assuming this
1056 selectable ultimately should proxy this column.
1057
1058 this is used to "ping" a derived selectable to add a new column
1059 to its .c. collection when a Column has been added to one of the
1060 Table objects it ultimately derives from.
1061
1062 If the given selectable hasn't populated its .c. collection yet,
1063 it should at least pass on the message to the contained selectables,
1064 but it will return None.
1065
1066 This method is currently used by Declarative to allow Table
1067 columns to be added to a partially constructed inheritance
1068 mapping that may have already produced joins. The method
1069 isn't public right now, as the full span of implications
1070 and/or caveats aren't yet clear.
1071
1072 It's also possible that this functionality could be invoked by
1073 default via an event, which would require that
1074 selectables maintain a weak referencing collection of all
1075 derivations.
1076
1077 """
1078 self._reset_column_collection()
1079
1080 def _anonymous_fromclause(
1081 self, *, name: Optional[str] = None, flat: bool = False
1082 ) -> FromClause:
1083 return self.alias(name=name)
1084
1085 if TYPE_CHECKING:
1086
1087 def self_group(
1088 self, against: Optional[OperatorType] = None
1089 ) -> Union[FromGrouping, Self]: ...
1090
1091
1092class NamedFromClause(FromClause[_KeyColCC_co]):
1093 """A :class:`.FromClause` that has a name.
1094
1095 Examples include tables, subqueries, CTEs, aliased tables.
1096
1097 .. versionadded:: 2.0
1098
1099 """
1100
1101 named_with_column = True
1102
1103 name: str
1104
1105 @util.preload_module("sqlalchemy.sql.sqltypes")
1106 def table_valued(self) -> TableValuedColumn[Any]:
1107 """Return a :class:`_sql.TableValuedColumn` object for this
1108 :class:`_expression.FromClause`.
1109
1110 A :class:`_sql.TableValuedColumn` is a :class:`_sql.ColumnElement` that
1111 represents a complete row in a table. Support for this construct is
1112 backend dependent, and is supported in various forms by backends
1113 such as PostgreSQL, Oracle Database and SQL Server.
1114
1115 E.g.:
1116
1117 .. sourcecode:: pycon+sql
1118
1119 >>> from sqlalchemy import select, column, func, table
1120 >>> a = table("a", column("id"), column("x"), column("y"))
1121 >>> stmt = select(func.row_to_json(a.table_valued()))
1122 >>> print(stmt)
1123 {printsql}SELECT row_to_json(a) AS row_to_json_1
1124 FROM a
1125
1126 .. versionadded:: 1.4.0b2
1127
1128 .. seealso::
1129
1130 :ref:`tutorial_functions` - in the :ref:`unified_tutorial`
1131
1132 """
1133 return TableValuedColumn(self, type_api.TABLEVALUE)
1134
1135 if TYPE_CHECKING:
1136
1137 def with_cols(
1138 self, type_: type[_TC_co]
1139 ) -> NamedFromClause[_TC_co]: ...
1140
1141
1142class SelectLabelStyle(Enum):
1143 """Label style constants that may be passed to
1144 :meth:`_sql.Select.set_label_style`."""
1145
1146 LABEL_STYLE_NONE = 0
1147 """Label style indicating no automatic labeling should be applied to the
1148 columns clause of a SELECT statement.
1149
1150 Below, the columns named ``columna`` are both rendered as is, meaning that
1151 the name ``columna`` can only refer to the first occurrence of this name
1152 within a result set, as well as if the statement were used as a subquery:
1153
1154 .. sourcecode:: pycon+sql
1155
1156 >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_NONE
1157 >>> table1 = table("table1", column("columna"), column("columnb"))
1158 >>> table2 = table("table2", column("columna"), column("columnc"))
1159 >>> print(
1160 ... select(table1, table2)
1161 ... .join(table2, true())
1162 ... .set_label_style(LABEL_STYLE_NONE)
1163 ... )
1164 {printsql}SELECT table1.columna, table1.columnb, table2.columna, table2.columnc
1165 FROM table1 JOIN table2 ON true
1166
1167 Used with the :meth:`_sql.Select.set_label_style` method.
1168
1169 .. versionadded:: 1.4
1170
1171 """ # noqa: E501
1172
1173 LABEL_STYLE_TABLENAME_PLUS_COL = 1
1174 """Label style indicating all columns should be labeled as
1175 ``<tablename>_<columnname>`` when generating the columns clause of a SELECT
1176 statement, to disambiguate same-named columns referenced from different
1177 tables, aliases, or subqueries.
1178
1179 Below, all column names are given a label so that the two same-named
1180 columns ``columna`` are disambiguated as ``table1_columna`` and
1181 ``table2_columna``:
1182
1183 .. sourcecode:: pycon+sql
1184
1185 >>> from sqlalchemy import (
1186 ... table,
1187 ... column,
1188 ... select,
1189 ... true,
1190 ... LABEL_STYLE_TABLENAME_PLUS_COL,
1191 ... )
1192 >>> table1 = table("table1", column("columna"), column("columnb"))
1193 >>> table2 = table("table2", column("columna"), column("columnc"))
1194 >>> print(
1195 ... select(table1, table2)
1196 ... .join(table2, true())
1197 ... .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
1198 ... )
1199 {printsql}SELECT table1.columna AS table1_columna, table1.columnb AS table1_columnb, table2.columna AS table2_columna, table2.columnc AS table2_columnc
1200 FROM table1 JOIN table2 ON true
1201
1202 Used with the :meth:`_sql.GenerativeSelect.set_label_style` method.
1203 Equivalent to the legacy method ``Select.apply_labels()``;
1204 :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` is SQLAlchemy's legacy
1205 auto-labeling style. :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` provides a
1206 less intrusive approach to disambiguation of same-named column expressions.
1207
1208
1209 .. versionadded:: 1.4
1210
1211 """ # noqa: E501
1212
1213 LABEL_STYLE_DISAMBIGUATE_ONLY = 2
1214 """Label style indicating that columns with a name that conflicts with
1215 an existing name should be labeled with a semi-anonymizing label
1216 when generating the columns clause of a SELECT statement.
1217
1218 Below, most column names are left unaffected, except for the second
1219 occurrence of the name ``columna``, which is labeled using the
1220 label ``columna_1`` to disambiguate it from that of ``tablea.columna``:
1221
1222 .. sourcecode:: pycon+sql
1223
1224 >>> from sqlalchemy import (
1225 ... table,
1226 ... column,
1227 ... select,
1228 ... true,
1229 ... LABEL_STYLE_DISAMBIGUATE_ONLY,
1230 ... )
1231 >>> table1 = table("table1", column("columna"), column("columnb"))
1232 >>> table2 = table("table2", column("columna"), column("columnc"))
1233 >>> print(
1234 ... select(table1, table2)
1235 ... .join(table2, true())
1236 ... .set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY)
1237 ... )
1238 {printsql}SELECT table1.columna, table1.columnb, table2.columna AS columna_1, table2.columnc
1239 FROM table1 JOIN table2 ON true
1240
1241 Used with the :meth:`_sql.GenerativeSelect.set_label_style` method,
1242 :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` is the default labeling style
1243 for all SELECT statements outside of :term:`1.x style` ORM queries.
1244
1245 .. versionadded:: 1.4
1246
1247 """ # noqa: E501
1248
1249 LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY
1250 """The default label style, refers to
1251 :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`.
1252
1253 .. versionadded:: 1.4
1254
1255 """
1256
1257 LABEL_STYLE_LEGACY_ORM = 3
1258
1259
1260(
1261 LABEL_STYLE_NONE,
1262 LABEL_STYLE_TABLENAME_PLUS_COL,
1263 LABEL_STYLE_DISAMBIGUATE_ONLY,
1264 _,
1265) = list(SelectLabelStyle)
1266
1267LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY
1268
1269
1270class Join(roles.DMLTableRole, FromClause[_KeyColCC_co]):
1271 """Represent a ``JOIN`` construct between two
1272 :class:`_expression.FromClause`
1273 elements.
1274
1275 The public constructor function for :class:`_expression.Join`
1276 is the module-level
1277 :func:`_expression.join()` function, as well as the
1278 :meth:`_expression.FromClause.join` method
1279 of any :class:`_expression.FromClause` (e.g. such as
1280 :class:`_schema.Table`).
1281
1282 .. seealso::
1283
1284 :func:`_expression.join`
1285
1286 :meth:`_expression.FromClause.join`
1287
1288 """
1289
1290 __visit_name__ = "join"
1291
1292 _traverse_internals: _TraverseInternalsType = [
1293 ("left", InternalTraversal.dp_clauseelement),
1294 ("right", InternalTraversal.dp_clauseelement),
1295 ("onclause", InternalTraversal.dp_clauseelement),
1296 ("isouter", InternalTraversal.dp_boolean),
1297 ("full", InternalTraversal.dp_boolean),
1298 ]
1299
1300 _is_join = True
1301
1302 left: FromClause
1303 right: FromClause
1304 onclause: Optional[ColumnElement[bool]]
1305 isouter: bool
1306 full: bool
1307
1308 def __init__(
1309 self,
1310 left: _FromClauseArgument,
1311 right: _FromClauseArgument,
1312 onclause: Optional[_OnClauseArgument] = None,
1313 isouter: bool = False,
1314 full: bool = False,
1315 ):
1316 """Construct a new :class:`_expression.Join`.
1317
1318 The usual entrypoint here is the :func:`_expression.join`
1319 function or the :meth:`_expression.FromClause.join` method of any
1320 :class:`_expression.FromClause` object.
1321
1322 """
1323
1324 # when deannotate was removed here, callcounts went up for ORM
1325 # compilation of eager joins, since there were more comparisons of
1326 # annotated objects. test_orm.py -> test_fetch_results
1327 # was therefore changed to show a more real-world use case, where the
1328 # compilation is cached; there's no change in post-cache callcounts.
1329 # callcounts for a single compilation in that particular test
1330 # that includes about eight joins about 1100 extra fn calls, from
1331 # 29200 -> 30373
1332
1333 self.left = coercions.expect(
1334 roles.FromClauseRole,
1335 left,
1336 )
1337 self.right = coercions.expect(
1338 roles.FromClauseRole,
1339 right,
1340 ).self_group()
1341
1342 if onclause is None:
1343 self.onclause = self._match_primaries(self.left, self.right)
1344 else:
1345 # note: taken from If91f61527236fd4d7ae3cad1f24c38be921c90ba
1346 # not merged yet
1347 self.onclause = coercions.expect(
1348 roles.OnClauseRole, onclause
1349 ).self_group(against=operators._asbool)
1350
1351 self.isouter = isouter
1352 self.full = full
1353
1354 @util.ro_non_memoized_property
1355 def description(self) -> str:
1356 return "Join object on %s(%d) and %s(%d)" % (
1357 self.left.description,
1358 id(self.left),
1359 self.right.description,
1360 id(self.right),
1361 )
1362
1363 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
1364 return (
1365 # use hash() to ensure direct comparison to annotated works
1366 # as well
1367 hash(fromclause) == hash(self)
1368 or self.left.is_derived_from(fromclause)
1369 or self.right.is_derived_from(fromclause)
1370 )
1371
1372 def self_group(
1373 self, against: Optional[OperatorType] = None
1374 ) -> FromGrouping:
1375 return FromGrouping(self)
1376
1377 @util.preload_module("sqlalchemy.sql.util")
1378 def _populate_column_collection(
1379 self,
1380 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
1381 primary_key: ColumnSet,
1382 foreign_keys: Set[KeyedColumnElement[Any]],
1383 ) -> None:
1384 sqlutil = util.preloaded.sql_util
1385 _columns: List[KeyedColumnElement[Any]] = [c for c in self.left.c] + [
1386 c for c in self.right.c
1387 ]
1388
1389 primary_key.extend(
1390 sqlutil.reduce_columns(
1391 (c for c in _columns if c.primary_key), self.onclause
1392 )
1393 )
1394 columns._populate_separate_keys(
1395 (col._tq_key_label, col) for col in _columns # type: ignore[misc]
1396 )
1397 foreign_keys.update(
1398 itertools.chain(*[col.foreign_keys for col in _columns]) # type: ignore[arg-type] # noqa: E501
1399 )
1400
1401 def _copy_internals(
1402 self, clone: _CloneCallableType = _clone, **kw: Any
1403 ) -> None:
1404 # see Select._copy_internals() for similar concept
1405
1406 # here we pre-clone "left" and "right" so that we can
1407 # determine the new FROM clauses
1408 all_the_froms = set(
1409 itertools.chain(
1410 _from_objects(self.left),
1411 _from_objects(self.right),
1412 )
1413 )
1414
1415 # run the clone on those. these will be placed in the
1416 # cache used by the clone function
1417 new_froms = {f: clone(f, **kw) for f in all_the_froms}
1418
1419 # set up a special replace function that will replace for
1420 # ColumnClause with parent table referring to those
1421 # replaced FromClause objects
1422 def replace(
1423 obj: Union[BinaryExpression[Any], ColumnClause[Any]],
1424 **kw: Any,
1425 ) -> Optional[KeyedColumnElement[Any]]:
1426 if isinstance(obj, ColumnClause) and obj.table in new_froms:
1427 newelem = new_froms[obj.table].corresponding_column(obj)
1428 return newelem
1429 return None
1430
1431 kw["replace"] = replace
1432
1433 # run normal _copy_internals. the clones for
1434 # left and right will come from the clone function's
1435 # cache
1436 super()._copy_internals(clone=clone, **kw)
1437
1438 self._reset_memoizations()
1439
1440 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
1441 super()._refresh_for_new_column(column)
1442 self.left._refresh_for_new_column(column)
1443 self.right._refresh_for_new_column(column)
1444
1445 def _match_primaries(
1446 self,
1447 left: FromClause,
1448 right: FromClause,
1449 ) -> ColumnElement[bool]:
1450 if isinstance(left, Join):
1451 left_right = left.right
1452 else:
1453 left_right = None
1454 return self._join_condition(left, right, a_subset=left_right)
1455
1456 @classmethod
1457 def _join_condition(
1458 cls,
1459 a: FromClause,
1460 b: FromClause,
1461 *,
1462 a_subset: Optional[FromClause] = None,
1463 consider_as_foreign_keys: Optional[
1464 AbstractSet[ColumnClause[Any]]
1465 ] = None,
1466 ) -> ColumnElement[bool]:
1467 """Create a join condition between two tables or selectables.
1468
1469 See sqlalchemy.sql.util.join_condition() for full docs.
1470
1471 """
1472 constraints = cls._joincond_scan_left_right(
1473 a, a_subset, b, consider_as_foreign_keys
1474 )
1475
1476 if len(constraints) > 1:
1477 cls._joincond_trim_constraints(
1478 a, b, constraints, consider_as_foreign_keys
1479 )
1480
1481 if len(constraints) == 0:
1482 if isinstance(b, FromGrouping):
1483 hint = (
1484 " Perhaps you meant to convert the right side to a "
1485 "subquery using alias()?"
1486 )
1487 else:
1488 hint = ""
1489 raise exc.NoForeignKeysError(
1490 "Can't find any foreign key relationships "
1491 "between '%s' and '%s'.%s"
1492 % (a.description, b.description, hint)
1493 )
1494
1495 crit = [(x == y) for x, y in list(constraints.values())[0]]
1496 if len(crit) == 1:
1497 return crit[0]
1498 else:
1499 return and_(*crit)
1500
1501 @classmethod
1502 def _can_join(
1503 cls,
1504 left: FromClause,
1505 right: FromClause,
1506 *,
1507 consider_as_foreign_keys: Optional[
1508 AbstractSet[ColumnClause[Any]]
1509 ] = None,
1510 ) -> bool:
1511 if isinstance(left, Join):
1512 left_right = left.right
1513 else:
1514 left_right = None
1515
1516 constraints = cls._joincond_scan_left_right(
1517 a=left,
1518 b=right,
1519 a_subset=left_right,
1520 consider_as_foreign_keys=consider_as_foreign_keys,
1521 )
1522
1523 return bool(constraints)
1524
1525 @classmethod
1526 @util.preload_module("sqlalchemy.sql.util")
1527 def _joincond_scan_left_right(
1528 cls,
1529 a: FromClause,
1530 a_subset: Optional[FromClause],
1531 b: FromClause,
1532 consider_as_foreign_keys: Optional[AbstractSet[ColumnClause[Any]]],
1533 ) -> collections.defaultdict[
1534 Optional[ForeignKeyConstraint],
1535 List[Tuple[ColumnClause[Any], ColumnClause[Any]]],
1536 ]:
1537 sql_util = util.preloaded.sql_util
1538
1539 a = coercions.expect(roles.FromClauseRole, a)
1540 b = coercions.expect(roles.FromClauseRole, b)
1541
1542 constraints: collections.defaultdict[
1543 Optional[ForeignKeyConstraint],
1544 List[Tuple[ColumnClause[Any], ColumnClause[Any]]],
1545 ] = collections.defaultdict(list)
1546
1547 for left in (a_subset, a):
1548 if left is None:
1549 continue
1550 for fk in sorted(
1551 b.foreign_keys,
1552 key=lambda fk: fk.parent._creation_order,
1553 ):
1554 if (
1555 consider_as_foreign_keys is not None
1556 and fk.parent not in consider_as_foreign_keys
1557 ):
1558 continue
1559 try:
1560 col = fk.get_referent(left)
1561 except exc.NoReferenceError as nrte:
1562 table_names = {t.name for t in sql_util.find_tables(left)}
1563 if nrte.table_name in table_names:
1564 raise
1565 else:
1566 continue
1567
1568 if col is not None:
1569 constraints[fk.constraint].append((col, fk.parent))
1570 if left is not b:
1571 for fk in sorted(
1572 left.foreign_keys,
1573 key=lambda fk: fk.parent._creation_order,
1574 ):
1575 if (
1576 consider_as_foreign_keys is not None
1577 and fk.parent not in consider_as_foreign_keys
1578 ):
1579 continue
1580 try:
1581 col = fk.get_referent(b)
1582 except exc.NoReferenceError as nrte:
1583 table_names = {t.name for t in sql_util.find_tables(b)}
1584 if nrte.table_name in table_names:
1585 raise
1586 else:
1587 continue
1588
1589 if col is not None:
1590 constraints[fk.constraint].append((col, fk.parent))
1591 if constraints:
1592 break
1593 return constraints
1594
1595 @classmethod
1596 def _joincond_trim_constraints(
1597 cls,
1598 a: FromClause,
1599 b: FromClause,
1600 constraints: Dict[Any, Any],
1601 consider_as_foreign_keys: Optional[Any],
1602 ) -> None:
1603 # more than one constraint matched. narrow down the list
1604 # to include just those FKCs that match exactly to
1605 # "consider_as_foreign_keys".
1606 if consider_as_foreign_keys:
1607 for const in list(constraints):
1608 if {f.parent for f in const.elements} != set(
1609 consider_as_foreign_keys
1610 ):
1611 del constraints[const]
1612
1613 # if still multiple constraints, but
1614 # they all refer to the exact same end result, use it.
1615 if len(constraints) > 1:
1616 dedupe = {tuple(crit) for crit in constraints.values()}
1617 if len(dedupe) == 1:
1618 key = list(constraints)[0]
1619 constraints = {key: constraints[key]}
1620
1621 if len(constraints) != 1:
1622 raise exc.AmbiguousForeignKeysError(
1623 "Can't determine join between '%s' and '%s'; "
1624 "tables have more than one foreign key "
1625 "constraint relationship between them. "
1626 "Please specify the 'onclause' of this "
1627 "join explicitly." % (a.description, b.description)
1628 )
1629
1630 @overload
1631 def select(
1632 self: Join[HasRowPos[Unpack[_Ts]]], # type: ignore[type-var]
1633 ) -> Select[Unpack[_Ts]]: ...
1634 @overload
1635 def select(self) -> Select[Unpack[TupleAny]]: ...
1636
1637 def select(self) -> Select[Unpack[TupleAny]]:
1638 r"""Create a :class:`_expression.Select` from this
1639 :class:`_expression.Join`.
1640
1641 E.g.::
1642
1643 stmt = table_a.join(table_b, table_a.c.id == table_b.c.a_id)
1644
1645 stmt = stmt.select()
1646
1647 The above will produce a SQL string resembling:
1648
1649 .. sourcecode:: sql
1650
1651 SELECT table_a.id, table_a.col, table_b.id, table_b.a_id
1652 FROM table_a JOIN table_b ON table_a.id = table_b.a_id
1653
1654 """
1655 return Select(self.left, self.right).select_from(self)
1656
1657 @staticmethod
1658 def _flat_element_name(
1659 name: Optional[str], element: FromClause
1660 ) -> Optional[str]:
1661 if name and isinstance(element, NamedFromClause):
1662 if isinstance(element.name, _anonymous_label):
1663 # an anonymous name can't be embedded within a new name;
1664 # alias the element anonymously, #13583
1665 return None
1666 return f"{name}_{element.name}"
1667 else:
1668 return name
1669
1670 @util.preload_module("sqlalchemy.sql.util")
1671 def _anonymous_fromclause(
1672 self, name: Optional[str] = None, flat: bool = False
1673 ) -> TODO_Any:
1674 sqlutil = util.preloaded.sql_util
1675 if flat:
1676 if isinstance(self.left, (FromGrouping, Join)):
1677 left_name = name # will recurse
1678 else:
1679 left_name = self._flat_element_name(name, self.left)
1680 if isinstance(self.right, (FromGrouping, Join)):
1681 right_name = name # will recurse
1682 else:
1683 right_name = self._flat_element_name(name, self.right)
1684 left_a, right_a = (
1685 self.left._anonymous_fromclause(name=left_name, flat=flat),
1686 self.right._anonymous_fromclause(name=right_name, flat=flat),
1687 )
1688 adapter = sqlutil.ClauseAdapter(left_a).chain(
1689 sqlutil.ClauseAdapter(right_a)
1690 )
1691
1692 return left_a.join(
1693 right_a,
1694 adapter.traverse(self.onclause),
1695 isouter=self.isouter,
1696 full=self.full,
1697 )
1698 else:
1699 return (
1700 self.select()
1701 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
1702 .correlate(None)
1703 .alias(name)
1704 )
1705
1706 @util.ro_non_memoized_property
1707 def _hide_froms(self) -> Iterable[FromClause]:
1708 return itertools.chain(
1709 *[_from_objects(x.left, x.right) for x in self._cloned_set]
1710 )
1711
1712 @util.ro_non_memoized_property
1713 def _from_objects(self) -> List[FromClause]:
1714 self_list: List[FromClause] = [self]
1715 return self_list + self.left._from_objects + self.right._from_objects
1716
1717 if TYPE_CHECKING:
1718
1719 def with_cols(self, type_: type[_TC_co]) -> Join[_TC_co]: ...
1720
1721
1722class NoInit:
1723 def __init__(self, *arg: Any, **kw: Any):
1724 raise NotImplementedError(
1725 "The %s class is not intended to be constructed "
1726 "directly. Please use the %s() standalone "
1727 "function or the %s() method available from appropriate "
1728 "selectable objects."
1729 % (
1730 self.__class__.__name__,
1731 self.__class__.__name__.lower(),
1732 self.__class__.__name__.lower(),
1733 )
1734 )
1735
1736
1737class LateralFromClause(NamedFromClause):
1738 """mark a FROM clause as being able to render directly as LATERAL"""
1739
1740
1741# FromClause ->
1742# AliasedReturnsRows
1743# -> Alias only for FromClause
1744# -> Subquery only for SelectBase
1745# -> CTE only for HasCTE -> SelectBase, DML
1746# -> Lateral -> FromClause, but we accept SelectBase
1747# w/ non-deprecated coercion
1748# -> TableSample -> only for FromClause
1749
1750
1751class AliasedReturnsRows(NoInit, NamedFromClause[_KeyColCC_co]):
1752 """Base class of aliases against tables, subqueries, and other
1753 selectables."""
1754
1755 _is_from_container = True
1756
1757 _supports_derived_columns = False
1758
1759 element: ReturnsRows
1760
1761 _traverse_internals: _TraverseInternalsType = [
1762 ("element", InternalTraversal.dp_clauseelement),
1763 ("name", InternalTraversal.dp_anon_name),
1764 ]
1765
1766 @classmethod
1767 def _construct(
1768 cls,
1769 selectable: Any,
1770 *,
1771 name: Optional[str] = None,
1772 **kw: Any,
1773 ) -> Self:
1774 obj = cls.__new__(cls)
1775 obj._init(selectable, name=name, **kw)
1776 return obj
1777
1778 def _init(self, selectable: Any, *, name: Optional[str] = None) -> None:
1779 self.element = coercions.expect(
1780 roles.ReturnsRowsRole, selectable, apply_propagate_attrs=self
1781 )
1782 self.element = selectable
1783 self._orig_name = name
1784 if name is None:
1785 if (
1786 isinstance(selectable, FromClause)
1787 and selectable.named_with_column
1788 ):
1789 name = getattr(selectable, "name", None)
1790 if isinstance(name, _anonymous_label):
1791 name = None
1792 name = _anonymous_label.safe_construct(
1793 os.urandom(10).hex(), name or "anon"
1794 )
1795 self.name = name
1796
1797 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
1798 super()._refresh_for_new_column(column)
1799 self.element._refresh_for_new_column(column)
1800
1801 def _populate_column_collection(
1802 self,
1803 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
1804 primary_key: ColumnSet,
1805 foreign_keys: Set[KeyedColumnElement[Any]],
1806 ) -> None:
1807 self.element._generate_fromclause_column_proxies(
1808 self, columns, primary_key=primary_key, foreign_keys=foreign_keys
1809 )
1810
1811 @util.ro_non_memoized_property
1812 def description(self) -> str:
1813 name = self.name
1814 if isinstance(name, _anonymous_label):
1815 return "anon_1"
1816
1817 return name
1818
1819 @util.ro_non_memoized_property
1820 def implicit_returning(self) -> bool:
1821 return self.element.implicit_returning # type: ignore[attr-defined, no-any-return] # noqa: E501
1822
1823 @property
1824 def original(self) -> ReturnsRows:
1825 """Legacy for dialects that are referring to Alias.original."""
1826 return self.element
1827
1828 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
1829 if fromclause in self._cloned_set:
1830 return True
1831 return self.element.is_derived_from(fromclause)
1832
1833 def _copy_internals(
1834 self, clone: _CloneCallableType = _clone, **kw: Any
1835 ) -> None:
1836 existing_element = self.element
1837
1838 super()._copy_internals(clone=clone, **kw)
1839
1840 # the element clone is usually against a Table that returns the
1841 # same object. don't reset exported .c. collections and other
1842 # memoized details if it was not changed. this saves a lot on
1843 # performance.
1844 if existing_element is not self.element:
1845 self._reset_column_collection()
1846
1847 @property
1848 def _from_objects(self) -> List[FromClause]:
1849 return [self]
1850
1851
1852class FromClauseAlias(AliasedReturnsRows[_KeyColCC_co]):
1853 element: FromClause[_KeyColCC_co]
1854
1855 @util.ro_non_memoized_property
1856 def description(self) -> str:
1857 name = self.name
1858 if isinstance(name, _anonymous_label):
1859 return f"Anonymous alias of {self.element.description}"
1860
1861 return name
1862
1863
1864class Alias(roles.DMLTableRole, FromClauseAlias[_KeyColCC_co]):
1865 """Represents an table or selectable alias (AS).
1866
1867 Represents an alias, as typically applied to any table or
1868 sub-select within a SQL statement using the ``AS`` keyword (or
1869 without the keyword on certain databases such as Oracle Database).
1870
1871 This object is constructed from the :func:`_expression.alias` module
1872 level function as well as the :meth:`_expression.FromClause.alias`
1873 method available
1874 on all :class:`_expression.FromClause` subclasses.
1875
1876 .. seealso::
1877
1878 :meth:`_expression.FromClause.alias`
1879
1880 """
1881
1882 __visit_name__ = "alias"
1883
1884 inherit_cache = True
1885
1886 element: FromClause[_KeyColCC_co]
1887
1888 @classmethod
1889 def _factory(
1890 cls,
1891 selectable: FromClause[_KeyColCC_co],
1892 name: Optional[str] = None,
1893 flat: bool = False,
1894 ) -> NamedFromClause[_KeyColCC_co]:
1895 # mypy refuses to see the overload that has this returning
1896 # NamedFromClause[Any]. Pylance sees it just fine.
1897 return coercions.expect(roles.FromClauseRole, selectable).alias( # type: ignore[no-any-return] # noqa: E501
1898 name=name, flat=flat
1899 )
1900
1901
1902class TableValuedAlias(LateralFromClause, Alias):
1903 """An alias against a "table valued" SQL function.
1904
1905 This construct provides for a SQL function that returns columns
1906 to be used in the FROM clause of a SELECT statement. The
1907 object is generated using the :meth:`_functions.FunctionElement.table_valued`
1908 method, e.g.:
1909
1910 .. sourcecode:: pycon+sql
1911
1912 >>> from sqlalchemy import select, func
1913 >>> fn = func.json_array_elements_text('["one", "two", "three"]').table_valued(
1914 ... "value"
1915 ... )
1916 >>> print(select(fn.c.value))
1917 {printsql}SELECT anon_1.value
1918 FROM json_array_elements_text(:json_array_elements_text_1) AS anon_1
1919
1920 .. versionadded:: 1.4.0b2
1921
1922 .. seealso::
1923
1924 :ref:`tutorial_functions_table_valued` - in the :ref:`unified_tutorial`
1925
1926 """ # noqa: E501
1927
1928 __visit_name__ = "table_valued_alias"
1929
1930 _supports_derived_columns = True
1931 _render_derived = False
1932 _render_derived_w_types = False
1933 joins_implicitly = False
1934
1935 _traverse_internals: _TraverseInternalsType = [
1936 ("element", InternalTraversal.dp_clauseelement),
1937 ("name", InternalTraversal.dp_anon_name),
1938 ("_tableval_type", InternalTraversal.dp_type),
1939 ("_render_derived", InternalTraversal.dp_boolean),
1940 ("_render_derived_w_types", InternalTraversal.dp_boolean),
1941 ]
1942
1943 def _init(
1944 self,
1945 selectable: Any,
1946 *,
1947 name: Optional[str] = None,
1948 table_value_type: Optional[TableValueType] = None,
1949 joins_implicitly: bool = False,
1950 ) -> None:
1951 super()._init(selectable, name=name)
1952
1953 self.joins_implicitly = joins_implicitly
1954 self._tableval_type = (
1955 type_api.TABLEVALUE
1956 if table_value_type is None
1957 else table_value_type
1958 )
1959
1960 @HasMemoized.memoized_attribute
1961 def column(self) -> TableValuedColumn[Any]:
1962 """Return a column expression representing this
1963 :class:`_sql.TableValuedAlias`.
1964
1965 This accessor is used to implement the
1966 :meth:`_functions.FunctionElement.column_valued` method. See that
1967 method for further details.
1968
1969 E.g.:
1970
1971 .. sourcecode:: pycon+sql
1972
1973 >>> print(select(func.some_func().table_valued("value").column))
1974 {printsql}SELECT anon_1 FROM some_func() AS anon_1
1975
1976 .. seealso::
1977
1978 :meth:`_functions.FunctionElement.column_valued`
1979
1980 """
1981
1982 return TableValuedColumn(self, self._tableval_type)
1983
1984 def alias(
1985 self, name: Optional[str] = None, flat: bool = False
1986 ) -> TableValuedAlias:
1987 """Return a new alias of this :class:`_sql.TableValuedAlias`.
1988
1989 This creates a distinct FROM object that will be distinguished
1990 from the original one when used in a SQL statement.
1991
1992 """
1993
1994 tva: TableValuedAlias = TableValuedAlias._construct(
1995 self,
1996 name=name,
1997 table_value_type=self._tableval_type,
1998 joins_implicitly=self.joins_implicitly,
1999 )
2000
2001 if self._render_derived:
2002 tva._render_derived = True
2003 tva._render_derived_w_types = self._render_derived_w_types
2004
2005 return tva
2006
2007 def lateral(self, name: Optional[str] = None) -> LateralFromClause:
2008 """Return a new :class:`_sql.TableValuedAlias` with the lateral flag
2009 set, so that it renders as LATERAL.
2010
2011 .. seealso::
2012
2013 :func:`_expression.lateral`
2014
2015 """
2016 tva = self.alias(name=name)
2017 tva._is_lateral = True
2018 return tva
2019
2020 def render_derived(
2021 self,
2022 name: Optional[str] = None,
2023 with_types: bool = False,
2024 ) -> TableValuedAlias:
2025 """Apply "render derived" to this :class:`_sql.TableValuedAlias`.
2026
2027 This has the effect of the individual column names listed out
2028 after the alias name in the "AS" sequence, e.g.:
2029
2030 .. sourcecode:: pycon+sql
2031
2032 >>> print(
2033 ... select(
2034 ... func.unnest(array(["one", "two", "three"]))
2035 ... .table_valued("x", with_ordinality="o")
2036 ... .render_derived()
2037 ... )
2038 ... )
2039 {printsql}SELECT anon_1.x, anon_1.o
2040 FROM unnest(ARRAY[%(param_1)s, %(param_2)s, %(param_3)s]) WITH ORDINALITY AS anon_1(x, o)
2041
2042 The ``with_types`` keyword will render column types inline within
2043 the alias expression (this syntax currently applies to the
2044 PostgreSQL database):
2045
2046 .. sourcecode:: pycon+sql
2047
2048 >>> print(
2049 ... select(
2050 ... func.json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]')
2051 ... .table_valued(column("a", Integer), column("b", String))
2052 ... .render_derived(with_types=True)
2053 ... )
2054 ... )
2055 {printsql}SELECT anon_1.a, anon_1.b FROM json_to_recordset(:json_to_recordset_1)
2056 AS anon_1(a INTEGER, b VARCHAR)
2057
2058 :param name: optional string name that will be applied to the alias
2059 generated. If left as None, a unique anonymizing name will be used.
2060
2061 :param with_types: if True, the derived columns will include the
2062 datatype specification with each column. This is a special syntax
2063 currently known to be required by PostgreSQL for some SQL functions.
2064
2065 """ # noqa: E501
2066
2067 # note: don't use the @_generative system here, keep a reference
2068 # to the original object. otherwise you can have reuse of the
2069 # python id() of the original which can cause name conflicts if
2070 # a new anon-name grabs the same identifier as the local anon-name
2071 # (just saw it happen on CI)
2072
2073 # construct against original to prevent memory growth
2074 # for repeated generations
2075 new_alias: TableValuedAlias = TableValuedAlias._construct(
2076 self.element,
2077 name=name,
2078 table_value_type=self._tableval_type,
2079 joins_implicitly=self.joins_implicitly,
2080 )
2081 new_alias._render_derived = True
2082 new_alias._render_derived_w_types = with_types
2083 return new_alias
2084
2085
2086class Lateral(FromClauseAlias, LateralFromClause):
2087 """Represent a LATERAL subquery.
2088
2089 This object is constructed from the :func:`_expression.lateral` module
2090 level function as well as the :meth:`_expression.FromClause.lateral`
2091 method available
2092 on all :class:`_expression.FromClause` subclasses.
2093
2094 While LATERAL is part of the SQL standard, currently only more recent
2095 PostgreSQL versions provide support for this keyword.
2096
2097 .. seealso::
2098
2099 :ref:`tutorial_lateral_correlation` - overview of usage.
2100
2101 """
2102
2103 __visit_name__ = "lateral"
2104 _is_lateral = True
2105
2106 inherit_cache = True
2107
2108 @classmethod
2109 def _factory(
2110 cls,
2111 selectable: Union[SelectBase, _FromClauseArgument],
2112 name: Optional[str] = None,
2113 ) -> LateralFromClause:
2114 return coercions.expect(
2115 roles.FromClauseRole, selectable, explicit_subquery=True
2116 ).lateral(name=name)
2117
2118
2119class TableSample(FromClauseAlias):
2120 """Represent a TABLESAMPLE clause.
2121
2122 This object is constructed from the :func:`_expression.tablesample` module
2123 level function as well as the :meth:`_expression.FromClause.tablesample`
2124 method
2125 available on all :class:`_expression.FromClause` subclasses.
2126
2127 .. seealso::
2128
2129 :func:`_expression.tablesample`
2130
2131 """
2132
2133 __visit_name__ = "tablesample"
2134
2135 _traverse_internals: _TraverseInternalsType = (
2136 AliasedReturnsRows._traverse_internals
2137 + [
2138 ("sampling", InternalTraversal.dp_clauseelement),
2139 ("seed", InternalTraversal.dp_clauseelement),
2140 ]
2141 )
2142
2143 @classmethod
2144 def _factory(
2145 cls,
2146 selectable: _FromClauseArgument,
2147 sampling: Union[float, Function[Any]],
2148 name: Optional[str] = None,
2149 seed: Optional[roles.ExpressionElementRole[Any]] = None,
2150 ) -> TableSample:
2151 return coercions.expect(roles.FromClauseRole, selectable).tablesample(
2152 sampling, name=name, seed=seed
2153 )
2154
2155 @util.preload_module("sqlalchemy.sql.functions")
2156 def _init( # type: ignore[override]
2157 self,
2158 selectable: Any,
2159 *,
2160 name: Optional[str] = None,
2161 sampling: Union[float, Function[Any]],
2162 seed: Optional[roles.ExpressionElementRole[Any]] = None,
2163 ) -> None:
2164 assert sampling is not None
2165 functions = util.preloaded.sql_functions
2166 if not isinstance(sampling, functions.Function):
2167 sampling = functions.func.system(sampling)
2168
2169 self.sampling: Function[Any] = sampling
2170 self.seed = seed
2171 super()._init(selectable, name=name)
2172
2173 def _get_method(self) -> Function[Any]:
2174 return self.sampling
2175
2176
2177class CTE(
2178 roles.DMLTableRole,
2179 roles.IsCTERole,
2180 Generative,
2181 HasPrefixes,
2182 HasSuffixes,
2183 AliasedReturnsRows[_KeyColCC_co],
2184):
2185 """Represent a Common Table Expression.
2186
2187 The :class:`_expression.CTE` object is obtained using the
2188 :meth:`_sql.SelectBase.cte` method from any SELECT statement. A less often
2189 available syntax also allows use of the :meth:`_sql.HasCTE.cte` method
2190 present on :term:`DML` constructs such as :class:`_sql.Insert`,
2191 :class:`_sql.Update` and
2192 :class:`_sql.Delete`. See the :meth:`_sql.HasCTE.cte` method for
2193 usage details on CTEs.
2194
2195 .. seealso::
2196
2197 :ref:`tutorial_subqueries_ctes` - in the 2.0 tutorial
2198
2199 :meth:`_sql.HasCTE.cte` - examples of calling styles
2200
2201 """
2202
2203 __visit_name__ = "cte"
2204
2205 _traverse_internals: _TraverseInternalsType = (
2206 AliasedReturnsRows._traverse_internals
2207 + [
2208 ("_cte_alias", InternalTraversal.dp_clauseelement),
2209 ("_restates", InternalTraversal.dp_clauseelement),
2210 ("recursive", InternalTraversal.dp_boolean),
2211 ("nesting", InternalTraversal.dp_boolean),
2212 ]
2213 + HasPrefixes._has_prefixes_traverse_internals
2214 + HasSuffixes._has_suffixes_traverse_internals
2215 )
2216
2217 element: HasCTE
2218
2219 @classmethod
2220 def _factory(
2221 cls,
2222 selectable: HasCTE,
2223 name: Optional[str] = None,
2224 recursive: bool = False,
2225 ) -> CTE:
2226 r"""Return a new :class:`_expression.CTE`,
2227 or Common Table Expression instance.
2228
2229 Please see :meth:`_expression.HasCTE.cte` for detail on CTE usage.
2230
2231 """
2232 return coercions.expect(roles.HasCTERole, selectable).cte(
2233 name=name, recursive=recursive
2234 )
2235
2236 def _init(
2237 self,
2238 selectable: HasCTE,
2239 *,
2240 name: Optional[str] = None,
2241 recursive: bool = False,
2242 nesting: bool = False,
2243 _cte_alias: Optional[CTE[_KeyColCC_co]] = None,
2244 _restates: Optional[CTE[_KeyColCC_co]] = None,
2245 _prefixes: Optional[Tuple[()]] = None,
2246 _suffixes: Optional[Tuple[()]] = None,
2247 ) -> None:
2248 self.recursive = recursive
2249 self.nesting = nesting
2250 self._cte_alias = _cte_alias
2251 # Keep recursivity reference with union/union_all
2252 self._restates = _restates
2253 if _prefixes:
2254 self._prefixes = _prefixes
2255 if _suffixes:
2256 self._suffixes = _suffixes
2257 super()._init(selectable, name=name)
2258
2259 def _populate_column_collection(
2260 self,
2261 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
2262 primary_key: ColumnSet,
2263 foreign_keys: Set[KeyedColumnElement[Any]],
2264 ) -> None:
2265 if self._cte_alias is not None:
2266 self._cte_alias._generate_fromclause_column_proxies(
2267 self,
2268 columns,
2269 primary_key=primary_key,
2270 foreign_keys=foreign_keys,
2271 )
2272 else:
2273 self.element._generate_fromclause_column_proxies(
2274 self,
2275 columns,
2276 primary_key=primary_key,
2277 foreign_keys=foreign_keys,
2278 )
2279
2280 def alias(
2281 self, name: Optional[str] = None, flat: bool = False
2282 ) -> CTE[_KeyColCC_co]:
2283 """Return an :class:`_expression.Alias` of this
2284 :class:`_expression.CTE`.
2285
2286 This method is a CTE-specific specialization of the
2287 :meth:`_expression.FromClause.alias` method.
2288
2289 .. seealso::
2290
2291 :ref:`tutorial_using_aliases`
2292
2293 :func:`_expression.alias`
2294
2295 """
2296 return CTE._construct(
2297 self.element,
2298 name=name,
2299 recursive=self.recursive,
2300 nesting=self.nesting,
2301 # an alias of an alias refers to the original CTE, #13583
2302 _cte_alias=(
2303 self._cte_alias if self._cte_alias is not None else self
2304 ),
2305 _prefixes=self._prefixes,
2306 _suffixes=self._suffixes,
2307 )
2308
2309 def union(
2310 self, *other: _SelectStatementForCompoundArgument[Any]
2311 ) -> CTE[_KeyColCC_co]:
2312 r"""Return a new :class:`_expression.CTE` with a SQL ``UNION``
2313 of the original CTE against the given selectables provided
2314 as positional arguments.
2315
2316 :param \*other: one or more elements with which to create a
2317 UNION.
2318
2319 .. versionchanged:: 1.4.28 multiple elements are now accepted.
2320
2321 .. seealso::
2322
2323 :meth:`_sql.HasCTE.cte` - examples of calling styles
2324
2325 """
2326 assert is_select_statement(
2327 self.element
2328 ), f"CTE element f{self.element} does not support union()"
2329
2330 return CTE._construct(
2331 self.element.union(*other),
2332 name=self.name,
2333 recursive=self.recursive,
2334 nesting=self.nesting,
2335 _restates=self,
2336 _prefixes=self._prefixes,
2337 _suffixes=self._suffixes,
2338 )
2339
2340 def union_all(
2341 self, *other: _SelectStatementForCompoundArgument[Any]
2342 ) -> CTE[_KeyColCC_co]:
2343 r"""Return a new :class:`_expression.CTE` with a SQL ``UNION ALL``
2344 of the original CTE against the given selectables provided
2345 as positional arguments.
2346
2347 :param \*other: one or more elements with which to create a
2348 UNION.
2349
2350 .. versionchanged:: 1.4.28 multiple elements are now accepted.
2351
2352 .. seealso::
2353
2354 :meth:`_sql.HasCTE.cte` - examples of calling styles
2355
2356 """
2357
2358 assert is_select_statement(
2359 self.element
2360 ), f"CTE element f{self.element} does not support union_all()"
2361
2362 return CTE._construct(
2363 self.element.union_all(*other),
2364 name=self.name,
2365 recursive=self.recursive,
2366 nesting=self.nesting,
2367 _restates=self,
2368 _prefixes=self._prefixes,
2369 _suffixes=self._suffixes,
2370 )
2371
2372 def _get_reference_cte(self) -> CTE[_KeyColCC_co]:
2373 """
2374 A recursive CTE is updated to attach the recursive part.
2375 Updated CTEs should still refer to the original CTE.
2376 This function returns this reference identifier.
2377 """
2378 return self._restates if self._restates is not None else self
2379
2380 if TYPE_CHECKING:
2381
2382 def with_cols(self, type_: type[_TC_co]) -> CTE[_TC_co]: ...
2383
2384
2385class _CTEOpts(NamedTuple):
2386 nesting: bool
2387
2388
2389class _ColumnsPlusNames(NamedTuple):
2390 required_label_name: Optional[str]
2391 """
2392 string label name, if non-None, must be rendered as a
2393 label, i.e. "AS <name>"
2394 """
2395
2396 proxy_key: Optional[str]
2397 """
2398 proxy_key that is to be part of the result map for this
2399 col. this is also the key in a fromclause.c or
2400 select.selected_columns collection
2401 """
2402
2403 fallback_label_name: Optional[str]
2404 """
2405 name that can be used to render an "AS <name>" when
2406 we have to render a label even though
2407 required_label_name was not given
2408 """
2409
2410 column: Union[ColumnElement[Any], AbstractTextClause]
2411 """
2412 the ColumnElement itself
2413 """
2414
2415 repeated: bool
2416 """
2417 True if this is a duplicate of a previous column
2418 in the list of columns
2419 """
2420
2421
2422class SelectsRows(ReturnsRows):
2423 """Sub-base of ReturnsRows for elements that deliver rows
2424 directly, namely SELECT and INSERT/UPDATE/DELETE..RETURNING"""
2425
2426 _label_style: SelectLabelStyle = LABEL_STYLE_NONE
2427
2428 def _generate_columns_plus_names(
2429 self,
2430 anon_for_dupe_key: bool,
2431 cols: Optional[_SelectIterable] = None,
2432 ) -> List[_ColumnsPlusNames]:
2433 """Generate column names as rendered in a SELECT statement by
2434 the compiler, as well as tokens used to populate the .c. collection
2435 on a :class:`.FromClause`.
2436
2437 This is distinct from the _column_naming_convention generator that's
2438 intended for population of the Select.selected_columns collection,
2439 different rules. the collection returned here calls upon the
2440 _column_naming_convention as well.
2441
2442 """
2443
2444 if cols is None:
2445 cols = self._all_selected_columns
2446
2447 key_naming_convention = SelectState._column_naming_convention(
2448 self._label_style
2449 )
2450
2451 names = {}
2452
2453 result: List[_ColumnsPlusNames] = []
2454 result_append = result.append
2455
2456 table_qualified = self._label_style is LABEL_STYLE_TABLENAME_PLUS_COL
2457 label_style_none = self._label_style is LABEL_STYLE_NONE
2458
2459 # a counter used for "dedupe" labels, which have double underscores
2460 # in them and are never referred by name; they only act
2461 # as positional placeholders. they need only be unique within
2462 # the single columns clause they're rendered within (required by
2463 # some dbs such as mysql). So their anon identity is tracked against
2464 # a fixed counter rather than hash() identity.
2465 dedupe_hash = 1
2466
2467 for c in cols:
2468 repeated = False
2469
2470 if not c._render_label_in_columns_clause:
2471 effective_name = required_label_name = fallback_label_name = (
2472 None
2473 )
2474 elif label_style_none:
2475 if TYPE_CHECKING:
2476 assert is_column_element(c)
2477
2478 effective_name = required_label_name = None
2479 fallback_label_name = c._non_anon_label or c._anon_name_label
2480 else:
2481 if TYPE_CHECKING:
2482 assert is_column_element(c)
2483
2484 if table_qualified:
2485 required_label_name = effective_name = (
2486 fallback_label_name
2487 ) = c._tq_label
2488 else:
2489 effective_name = fallback_label_name = c._non_anon_label
2490 required_label_name = None
2491
2492 if effective_name is None:
2493 # it seems like this could be _proxy_key and we would
2494 # not need _expression_label but it isn't
2495 # giving us a clue when to use anon_label instead
2496 expr_label = c._expression_label
2497 if expr_label is None:
2498 repeated = c._anon_name_label in names
2499 names[c._anon_name_label] = c
2500 effective_name = required_label_name = None
2501
2502 if repeated:
2503 # here, "required_label_name" is sent as
2504 # "None" and "fallback_label_name" is sent.
2505 if table_qualified:
2506 fallback_label_name = (
2507 c._dedupe_anon_tq_label_idx(dedupe_hash)
2508 )
2509 dedupe_hash += 1
2510 else:
2511 fallback_label_name = c._dedupe_anon_label_idx(
2512 dedupe_hash
2513 )
2514 dedupe_hash += 1
2515 else:
2516 fallback_label_name = c._anon_name_label
2517 else:
2518 required_label_name = effective_name = (
2519 fallback_label_name
2520 ) = expr_label
2521
2522 if effective_name is not None:
2523 if TYPE_CHECKING:
2524 assert is_column_element(c)
2525
2526 if effective_name in names:
2527 # when looking to see if names[name] is the same column as
2528 # c, use hash(), so that an annotated version of the column
2529 # is seen as the same as the non-annotated
2530 if hash(names[effective_name]) != hash(c):
2531 # different column under the same name. apply
2532 # disambiguating label
2533 if table_qualified:
2534 required_label_name = fallback_label_name = (
2535 c._anon_tq_label
2536 )
2537 else:
2538 required_label_name = fallback_label_name = (
2539 c._anon_name_label
2540 )
2541
2542 if anon_for_dupe_key and required_label_name in names:
2543 # here, c._anon_tq_label is definitely unique to
2544 # that column identity (or annotated version), so
2545 # this should always be true.
2546 # this is also an infrequent codepath because
2547 # you need two levels of duplication to be here
2548 assert hash(names[required_label_name]) == hash(c)
2549
2550 # the column under the disambiguating label is
2551 # already present. apply the "dedupe" label to
2552 # subsequent occurrences of the column so that the
2553 # original stays non-ambiguous
2554 if table_qualified:
2555 required_label_name = fallback_label_name = (
2556 c._dedupe_anon_tq_label_idx(dedupe_hash)
2557 )
2558 dedupe_hash += 1
2559 else:
2560 required_label_name = fallback_label_name = (
2561 c._dedupe_anon_label_idx(dedupe_hash)
2562 )
2563 dedupe_hash += 1
2564 repeated = True
2565 else:
2566 names[required_label_name] = c
2567 elif anon_for_dupe_key:
2568 # same column under the same name. apply the "dedupe"
2569 # label so that the original stays non-ambiguous
2570 if table_qualified:
2571 required_label_name = fallback_label_name = (
2572 c._dedupe_anon_tq_label_idx(dedupe_hash)
2573 )
2574 dedupe_hash += 1
2575 else:
2576 required_label_name = fallback_label_name = (
2577 c._dedupe_anon_label_idx(dedupe_hash)
2578 )
2579 dedupe_hash += 1
2580 repeated = True
2581 else:
2582 names[effective_name] = c
2583
2584 result_append(
2585 _ColumnsPlusNames(
2586 required_label_name,
2587 key_naming_convention(c),
2588 fallback_label_name,
2589 c,
2590 repeated,
2591 )
2592 )
2593
2594 return result
2595
2596
2597class HasCTE(roles.HasCTERole, SelectsRows):
2598 """Mixin that declares a class to include CTE support."""
2599
2600 _has_ctes_traverse_internals: _TraverseInternalsType = [
2601 ("_independent_ctes", InternalTraversal.dp_clauseelement_list),
2602 ("_independent_ctes_opts", InternalTraversal.dp_plain_obj),
2603 ]
2604
2605 _independent_ctes: Tuple[CTE, ...] = ()
2606 _independent_ctes_opts: Tuple[_CTEOpts, ...] = ()
2607
2608 name_cte_columns: bool = False
2609 """indicates if this HasCTE as contained within a CTE should compel the CTE
2610 to render the column names of this object in the WITH clause.
2611
2612 .. versionadded:: 2.0.42
2613
2614 """
2615
2616 @_generative
2617 def add_cte(self, *ctes: CTE, nest_here: bool = False) -> Self:
2618 r"""Add one or more :class:`_sql.CTE` constructs to this statement.
2619
2620 This method will associate the given :class:`_sql.CTE` constructs with
2621 the parent statement such that they will each be unconditionally
2622 rendered in the WITH clause of the final statement, even if not
2623 referenced elsewhere within the statement or any sub-selects.
2624
2625 The optional :paramref:`.HasCTE.add_cte.nest_here` parameter when set
2626 to True will have the effect that each given :class:`_sql.CTE` will
2627 render in a WITH clause rendered directly along with this statement,
2628 rather than being moved to the top of the ultimate rendered statement,
2629 even if this statement is rendered as a subquery within a larger
2630 statement.
2631
2632 This method has two general uses. One is to embed CTE statements that
2633 serve some purpose without being referenced explicitly, such as the use
2634 case of embedding a DML statement such as an INSERT or UPDATE as a CTE
2635 inline with a primary statement that may draw from its results
2636 indirectly. The other is to provide control over the exact placement
2637 of a particular series of CTE constructs that should remain rendered
2638 directly in terms of a particular statement that may be nested in a
2639 larger statement.
2640
2641 E.g.::
2642
2643 from sqlalchemy import table, column, select
2644
2645 t = table("t", column("c1"), column("c2"))
2646
2647 ins = t.insert().values({"c1": "x", "c2": "y"}).cte()
2648
2649 stmt = select(t).add_cte(ins)
2650
2651 Would render:
2652
2653 .. sourcecode:: sql
2654
2655 WITH anon_1 AS (
2656 INSERT INTO t (c1, c2) VALUES (:param_1, :param_2)
2657 )
2658 SELECT t.c1, t.c2
2659 FROM t
2660
2661 Above, the "anon_1" CTE is not referenced in the SELECT
2662 statement, however still accomplishes the task of running an INSERT
2663 statement.
2664
2665 Similarly in a DML-related context, using the PostgreSQL
2666 :class:`_postgresql.Insert` construct to generate an "upsert"::
2667
2668 from sqlalchemy import table, column
2669 from sqlalchemy.dialects.postgresql import insert
2670
2671 t = table("t", column("c1"), column("c2"))
2672
2673 delete_statement_cte = t.delete().where(t.c.c1 < 1).cte("deletions")
2674
2675 insert_stmt = insert(t).values({"c1": 1, "c2": 2})
2676 update_statement = insert_stmt.on_conflict_do_update(
2677 index_elements=[t.c.c1],
2678 set_={
2679 "c1": insert_stmt.excluded.c1,
2680 "c2": insert_stmt.excluded.c2,
2681 },
2682 ).add_cte(delete_statement_cte)
2683
2684 print(update_statement)
2685
2686 The above statement renders as:
2687
2688 .. sourcecode:: sql
2689
2690 WITH deletions AS (
2691 DELETE FROM t WHERE t.c1 < %(c1_1)s
2692 )
2693 INSERT INTO t (c1, c2) VALUES (%(c1)s, %(c2)s)
2694 ON CONFLICT (c1) DO UPDATE SET c1 = excluded.c1, c2 = excluded.c2
2695
2696 .. versionadded:: 1.4.21
2697
2698 :param \*ctes: zero or more :class:`.CTE` constructs.
2699
2700 .. versionchanged:: 2.0 Multiple CTE instances are accepted
2701
2702 :param nest_here: if True, the given CTE or CTEs will be rendered
2703 as though they specified the :paramref:`.HasCTE.cte.nesting` flag
2704 to ``True`` when they were added to this :class:`.HasCTE`.
2705 Assuming the given CTEs are not referenced in an outer-enclosing
2706 statement as well, the CTEs given should render at the level of
2707 this statement when this flag is given.
2708
2709 .. versionadded:: 2.0
2710
2711 .. seealso::
2712
2713 :paramref:`.HasCTE.cte.nesting`
2714
2715
2716 """ # noqa: E501
2717 opt = _CTEOpts(nest_here)
2718 for cte in ctes:
2719 cte = coercions.expect(roles.IsCTERole, cte)
2720 self._independent_ctes += (cte,)
2721 self._independent_ctes_opts += (opt,)
2722 return self
2723
2724 def cte(
2725 self,
2726 name: Optional[str] = None,
2727 recursive: bool = False,
2728 nesting: bool = False,
2729 ) -> CTE:
2730 r"""Return a new :class:`_expression.CTE`,
2731 or Common Table Expression instance.
2732
2733 Common table expressions are a SQL standard whereby SELECT
2734 statements can draw upon secondary statements specified along
2735 with the primary statement, using a clause called "WITH".
2736 Special semantics regarding UNION can also be employed to
2737 allow "recursive" queries, where a SELECT statement can draw
2738 upon the set of rows that have previously been selected.
2739
2740 CTEs can also be applied to DML constructs UPDATE, INSERT
2741 and DELETE on some databases, both as a source of CTE rows
2742 when combined with RETURNING, as well as a consumer of
2743 CTE rows.
2744
2745 SQLAlchemy detects :class:`_expression.CTE` objects, which are treated
2746 similarly to :class:`_expression.Alias` objects, as special elements
2747 to be delivered to the FROM clause of the statement as well
2748 as to a WITH clause at the top of the statement.
2749
2750 For special prefixes such as PostgreSQL "MATERIALIZED" and
2751 "NOT MATERIALIZED", the :meth:`_expression.CTE.prefix_with`
2752 method may be
2753 used to establish these.
2754
2755 :param name: name given to the common table expression. Like
2756 :meth:`_expression.FromClause.alias`, the name can be left as
2757 ``None`` in which case an anonymous symbol will be used at query
2758 compile time.
2759 :param recursive: if ``True``, will render ``WITH RECURSIVE``.
2760 A recursive common table expression is intended to be used in
2761 conjunction with UNION ALL in order to derive rows
2762 from those already selected.
2763 :param nesting: if ``True``, will render the CTE locally to the
2764 statement in which it is referenced. For more complex scenarios,
2765 the :meth:`.HasCTE.add_cte` method using the
2766 :paramref:`.HasCTE.add_cte.nest_here`
2767 parameter may also be used to more carefully
2768 control the exact placement of a particular CTE.
2769
2770 .. versionadded:: 1.4.24
2771
2772 .. seealso::
2773
2774 :meth:`.HasCTE.add_cte`
2775
2776 The following examples include two from PostgreSQL's documentation at
2777 https://www.postgresql.org/docs/current/static/queries-with.html,
2778 as well as additional examples.
2779
2780 Example 1, non recursive::
2781
2782 from sqlalchemy import (
2783 Table,
2784 Column,
2785 String,
2786 Integer,
2787 MetaData,
2788 select,
2789 func,
2790 )
2791
2792 metadata = MetaData()
2793
2794 orders = Table(
2795 "orders",
2796 metadata,
2797 Column("region", String),
2798 Column("amount", Integer),
2799 Column("product", String),
2800 Column("quantity", Integer),
2801 )
2802
2803 regional_sales = (
2804 select(orders.c.region, func.sum(orders.c.amount).label("total_sales"))
2805 .group_by(orders.c.region)
2806 .cte("regional_sales")
2807 )
2808
2809
2810 top_regions = (
2811 select(regional_sales.c.region)
2812 .where(
2813 regional_sales.c.total_sales
2814 > select(func.sum(regional_sales.c.total_sales) / 10)
2815 )
2816 .cte("top_regions")
2817 )
2818
2819 statement = (
2820 select(
2821 orders.c.region,
2822 orders.c.product,
2823 func.sum(orders.c.quantity).label("product_units"),
2824 func.sum(orders.c.amount).label("product_sales"),
2825 )
2826 .where(orders.c.region.in_(select(top_regions.c.region)))
2827 .group_by(orders.c.region, orders.c.product)
2828 )
2829
2830 result = conn.execute(statement).fetchall()
2831
2832 Example 2, WITH RECURSIVE::
2833
2834 from sqlalchemy import (
2835 Table,
2836 Column,
2837 String,
2838 Integer,
2839 MetaData,
2840 select,
2841 func,
2842 )
2843
2844 metadata = MetaData()
2845
2846 parts = Table(
2847 "parts",
2848 metadata,
2849 Column("part", String),
2850 Column("sub_part", String),
2851 Column("quantity", Integer),
2852 )
2853
2854 included_parts = (
2855 select(parts.c.sub_part, parts.c.part, parts.c.quantity)
2856 .where(parts.c.part == "our part")
2857 .cte(recursive=True)
2858 )
2859
2860
2861 incl_alias = included_parts.alias()
2862 parts_alias = parts.alias()
2863 included_parts = included_parts.union_all(
2864 select(
2865 parts_alias.c.sub_part, parts_alias.c.part, parts_alias.c.quantity
2866 ).where(parts_alias.c.part == incl_alias.c.sub_part)
2867 )
2868
2869 statement = select(
2870 included_parts.c.sub_part,
2871 func.sum(included_parts.c.quantity).label("total_quantity"),
2872 ).group_by(included_parts.c.sub_part)
2873
2874 result = conn.execute(statement).fetchall()
2875
2876 Example 3, an upsert using UPDATE and INSERT with CTEs::
2877
2878 from datetime import date
2879 from sqlalchemy import (
2880 MetaData,
2881 Table,
2882 Column,
2883 Integer,
2884 Date,
2885 select,
2886 literal,
2887 and_,
2888 exists,
2889 )
2890
2891 metadata = MetaData()
2892
2893 visitors = Table(
2894 "visitors",
2895 metadata,
2896 Column("product_id", Integer, primary_key=True),
2897 Column("date", Date, primary_key=True),
2898 Column("count", Integer),
2899 )
2900
2901 # add 5 visitors for the product_id == 1
2902 product_id = 1
2903 day = date.today()
2904 count = 5
2905
2906 update_cte = (
2907 visitors.update()
2908 .where(
2909 and_(visitors.c.product_id == product_id, visitors.c.date == day)
2910 )
2911 .values(count=visitors.c.count + count)
2912 .returning(literal(1))
2913 .cte("update_cte")
2914 )
2915
2916 upsert = visitors.insert().from_select(
2917 [visitors.c.product_id, visitors.c.date, visitors.c.count],
2918 select(literal(product_id), literal(day), literal(count)).where(
2919 ~exists(update_cte.select())
2920 ),
2921 )
2922
2923 connection.execute(upsert)
2924
2925 Example 4, Nesting CTE (SQLAlchemy 1.4.24 and above)::
2926
2927 value_a = select(literal("root").label("n")).cte("value_a")
2928
2929 # A nested CTE with the same name as the root one
2930 value_a_nested = select(literal("nesting").label("n")).cte(
2931 "value_a", nesting=True
2932 )
2933
2934 # Nesting CTEs takes ascendency locally
2935 # over the CTEs at a higher level
2936 value_b = select(value_a_nested.c.n).cte("value_b")
2937
2938 value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b"))
2939
2940 The above query will render the second CTE nested inside the first,
2941 shown with inline parameters below as:
2942
2943 .. sourcecode:: sql
2944
2945 WITH
2946 value_a AS
2947 (SELECT 'root' AS n),
2948 value_b AS
2949 (WITH value_a AS
2950 (SELECT 'nesting' AS n)
2951 SELECT value_a.n AS n FROM value_a)
2952 SELECT value_a.n AS a, value_b.n AS b
2953 FROM value_a, value_b
2954
2955 The same CTE can be set up using the :meth:`.HasCTE.add_cte` method
2956 as follows (SQLAlchemy 2.0 and above)::
2957
2958 value_a = select(literal("root").label("n")).cte("value_a")
2959
2960 # A nested CTE with the same name as the root one
2961 value_a_nested = select(literal("nesting").label("n")).cte("value_a")
2962
2963 # Nesting CTEs takes ascendency locally
2964 # over the CTEs at a higher level
2965 value_b = (
2966 select(value_a_nested.c.n)
2967 .add_cte(value_a_nested, nest_here=True)
2968 .cte("value_b")
2969 )
2970
2971 value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b"))
2972
2973 Example 5, Non-Linear CTE (SQLAlchemy 1.4.28 and above)::
2974
2975 edge = Table(
2976 "edge",
2977 metadata,
2978 Column("id", Integer, primary_key=True),
2979 Column("left", Integer),
2980 Column("right", Integer),
2981 )
2982
2983 root_node = select(literal(1).label("node")).cte("nodes", recursive=True)
2984
2985 left_edge = select(edge.c.left).join(
2986 root_node, edge.c.right == root_node.c.node
2987 )
2988 right_edge = select(edge.c.right).join(
2989 root_node, edge.c.left == root_node.c.node
2990 )
2991
2992 subgraph_cte = root_node.union(left_edge, right_edge)
2993
2994 subgraph = select(subgraph_cte)
2995
2996 The above query will render 2 UNIONs inside the recursive CTE:
2997
2998 .. sourcecode:: sql
2999
3000 WITH RECURSIVE nodes(node) AS (
3001 SELECT 1 AS node
3002 UNION
3003 SELECT edge."left" AS "left"
3004 FROM edge JOIN nodes ON edge."right" = nodes.node
3005 UNION
3006 SELECT edge."right" AS "right"
3007 FROM edge JOIN nodes ON edge."left" = nodes.node
3008 )
3009 SELECT nodes.node FROM nodes
3010
3011 .. seealso::
3012
3013 :meth:`_orm.Query.cte` - ORM version of
3014 :meth:`_expression.HasCTE.cte`.
3015
3016 """ # noqa: E501
3017 return CTE._construct(
3018 self, name=name, recursive=recursive, nesting=nesting
3019 )
3020
3021
3022class Subquery(AliasedReturnsRows[_KeyColCC_co]):
3023 """Represent a subquery of a SELECT.
3024
3025 A :class:`.Subquery` is created by invoking the
3026 :meth:`_expression.SelectBase.subquery` method, or for convenience the
3027 :meth:`_expression.SelectBase.alias` method, on any
3028 :class:`_expression.SelectBase` subclass
3029 which includes :class:`_expression.Select`,
3030 :class:`_expression.CompoundSelect`, and
3031 :class:`_expression.TextualSelect`. As rendered in a FROM clause,
3032 it represents the
3033 body of the SELECT statement inside of parenthesis, followed by the usual
3034 "AS <somename>" that defines all "alias" objects.
3035
3036 The :class:`.Subquery` object is very similar to the
3037 :class:`_expression.Alias`
3038 object and can be used in an equivalent way. The difference between
3039 :class:`_expression.Alias` and :class:`.Subquery` is that
3040 :class:`_expression.Alias` always
3041 contains a :class:`_expression.FromClause` object whereas
3042 :class:`.Subquery`
3043 always contains a :class:`_expression.SelectBase` object.
3044
3045 .. versionadded:: 1.4 The :class:`.Subquery` class was added which now
3046 serves the purpose of providing an aliased version of a SELECT
3047 statement.
3048
3049 """
3050
3051 __visit_name__ = "subquery"
3052
3053 _is_subquery = True
3054
3055 inherit_cache = True
3056
3057 element: SelectBase
3058
3059 @classmethod
3060 def _factory(
3061 cls, selectable: SelectBase, name: Optional[str] = None
3062 ) -> Subquery:
3063 """Return a :class:`.Subquery` object."""
3064
3065 return coercions.expect(
3066 roles.SelectStatementRole, selectable
3067 ).subquery(name=name)
3068
3069 @util.deprecated(
3070 "1.4",
3071 "The :meth:`.Subquery.as_scalar` method, which was previously "
3072 "``Alias.as_scalar()`` prior to version 1.4, is deprecated and "
3073 "will be removed in a future release; Please use the "
3074 ":meth:`_expression.Select.scalar_subquery` method of the "
3075 ":func:`_expression.select` "
3076 "construct before constructing a subquery object, or with the ORM "
3077 "use the :meth:`_query.Query.scalar_subquery` method.",
3078 )
3079 def as_scalar(self) -> ScalarSelect[Any]:
3080 return self.element.set_label_style(LABEL_STYLE_NONE).scalar_subquery()
3081
3082
3083class FromGrouping(GroupedElement, FromClause[_KeyColCC_co]):
3084 """Represent a grouping of a FROM clause"""
3085
3086 _traverse_internals: _TraverseInternalsType = [
3087 ("element", InternalTraversal.dp_clauseelement)
3088 ]
3089
3090 element: FromClause[_KeyColCC_co]
3091
3092 def __init__(self, element: FromClause[_KeyColCC_co]):
3093 self.element = coercions.expect(roles.FromClauseRole, element)
3094
3095 @util.ro_non_memoized_property
3096 def columns(self) -> _KeyColCC_co:
3097 return self.element.columns
3098
3099 @util.ro_non_memoized_property
3100 def c(self) -> _KeyColCC_co:
3101 return self.element.columns
3102
3103 @property
3104 def primary_key(self) -> Iterable[NamedColumn[Any]]:
3105 return self.element.primary_key
3106
3107 @property
3108 def foreign_keys(self) -> Iterable[ForeignKey]:
3109 return self.element.foreign_keys
3110
3111 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
3112 return self.element.is_derived_from(fromclause)
3113
3114 def alias(
3115 self, name: Optional[str] = None, flat: bool = False
3116 ) -> NamedFromGrouping[_KeyColCC_co]:
3117 return NamedFromGrouping(self.element.alias(name=name, flat=flat))
3118
3119 def _anonymous_fromclause(
3120 self, *, name: Optional[str] = None, flat: bool = False
3121 ) -> FromGrouping:
3122 return FromGrouping(
3123 self.element._anonymous_fromclause(name=name, flat=flat)
3124 )
3125
3126 @util.ro_non_memoized_property
3127 def _hide_froms(self) -> Iterable[FromClause]:
3128 return self.element._hide_froms
3129
3130 @util.ro_non_memoized_property
3131 def _from_objects(self) -> List[FromClause]:
3132 return self.element._from_objects
3133
3134 def __getstate__(self) -> Dict[str, FromClause[_KeyColCC_co]]:
3135 return {"element": self.element}
3136
3137 def __setstate__(self, state: Dict[str, FromClause[_KeyColCC_co]]) -> None:
3138 self.element = state["element"]
3139
3140 if TYPE_CHECKING:
3141
3142 def self_group(
3143 self, against: Optional[OperatorType] = None
3144 ) -> Self: ...
3145
3146
3147class NamedFromGrouping(
3148 FromGrouping[_KeyColCC_co], NamedFromClause[_KeyColCC_co]
3149):
3150 """represent a grouping of a named FROM clause
3151
3152 .. versionadded:: 2.0
3153
3154 """
3155
3156 inherit_cache = True
3157
3158 if TYPE_CHECKING:
3159
3160 def self_group(
3161 self, against: Optional[OperatorType] = None
3162 ) -> Self: ...
3163
3164
3165class TableClause(
3166 roles.DMLTableRole, Immutable, NamedFromClause[_ColClauseCC_co]
3167):
3168 """Represents a minimal "table" construct.
3169
3170 This is a lightweight table object that has only a name, a
3171 collection of columns, which are typically produced
3172 by the :func:`_expression.column` function, and a schema::
3173
3174 from sqlalchemy import table, column
3175
3176 user = table(
3177 "user",
3178 column("id"),
3179 column("name"),
3180 column("description"),
3181 )
3182
3183 The :class:`_expression.TableClause` construct serves as the base for
3184 the more commonly used :class:`_schema.Table` object, providing
3185 the usual set of :class:`_expression.FromClause` services including
3186 the ``.c.`` collection and statement generation methods.
3187
3188 It does **not** provide all the additional schema-level services
3189 of :class:`_schema.Table`, including constraints, references to other
3190 tables, or support for :class:`_schema.MetaData`-level services.
3191 It's useful
3192 on its own as an ad-hoc construct used to generate quick SQL
3193 statements when a more fully fledged :class:`_schema.Table`
3194 is not on hand.
3195
3196 """
3197
3198 __visit_name__ = "table"
3199
3200 _traverse_internals: _TraverseInternalsType = [
3201 (
3202 "columns",
3203 InternalTraversal.dp_fromclause_canonical_column_collection,
3204 ),
3205 ("name", InternalTraversal.dp_string),
3206 ("schema", InternalTraversal.dp_string),
3207 ]
3208
3209 _is_table = True
3210
3211 fullname: str
3212
3213 implicit_returning = False
3214 """:class:`_expression.TableClause`
3215 doesn't support having a primary key or column
3216 -level defaults, so implicit returning doesn't apply."""
3217
3218 _columns: DedupeColumnCollection[ColumnClause[Any]]
3219
3220 @util.ro_memoized_property
3221 def _autoincrement_column(self) -> Optional[ColumnClause[Any]]:
3222 """No PK or default support so no autoincrement column."""
3223 return None
3224
3225 def __init__(self, name: str, *columns: ColumnClause[Any], **kw: Any):
3226 super().__init__()
3227 self.name = name
3228 self._columns = DedupeColumnCollection() # type: ignore[unused-ignore]
3229 self.primary_key = ColumnSet() # type: ignore[misc]
3230 self.foreign_keys = set() # type: ignore[misc]
3231 for c in columns:
3232 self.append_column(c)
3233
3234 schema = kw.pop("schema", None)
3235 if schema is not None:
3236 self.schema = schema
3237 if self.schema is not None:
3238 self.fullname = "%s.%s" % (self.schema, self.name)
3239 else:
3240 self.fullname = self.name
3241 if kw:
3242 raise exc.ArgumentError("Unsupported argument(s): %s" % list(kw))
3243
3244 if TYPE_CHECKING:
3245
3246 def with_cols(self, type_: type[_TC_co]) -> TableClause[_TC_co]: ...
3247
3248 def __str__(self) -> str:
3249 if self.schema is not None:
3250 return self.schema + "." + self.name
3251 else:
3252 return self.name
3253
3254 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
3255 pass
3256
3257 @util.ro_memoized_property
3258 def description(self) -> str:
3259 return self.name
3260
3261 def _insert_col_impl(
3262 self,
3263 c: ColumnClause[Any],
3264 *,
3265 index: Optional[int] = None,
3266 ) -> None:
3267 existing = c.table
3268 if existing is not None and existing is not self:
3269 raise exc.ArgumentError(
3270 "column object '%s' already assigned to table '%s'"
3271 % (c.key, existing)
3272 )
3273 self._columns.add(c, index=index)
3274 c.table = self
3275
3276 def append_column(self, column: ColumnClause[Any]) -> None:
3277 """Append a :class:`.ColumnClause` to this :class:`.TableClause`."""
3278 self._insert_col_impl(column)
3279
3280 def insert_column(self, column: ColumnClause[Any], index: int) -> None:
3281 """Insert a :class:`.ColumnClause` to this :class:`.TableClause` at
3282 a specific position.
3283
3284 .. versionadded:: 2.1
3285
3286 """
3287 self._insert_col_impl(column, index=index)
3288
3289 @util.preload_module("sqlalchemy.sql.dml")
3290 def insert(self) -> util.preloaded.sql_dml.Insert:
3291 """Generate an :class:`_sql.Insert` construct against this
3292 :class:`_expression.TableClause`.
3293
3294 E.g.::
3295
3296 table.insert().values(name="foo")
3297
3298 See :func:`_expression.insert` for argument and usage information.
3299
3300 """
3301
3302 return util.preloaded.sql_dml.Insert(self)
3303
3304 @util.preload_module("sqlalchemy.sql.dml")
3305 def update(self) -> Update:
3306 """Generate an :func:`_expression.update` construct against this
3307 :class:`_expression.TableClause`.
3308
3309 E.g.::
3310
3311 table.update().where(table.c.id == 7).values(name="foo")
3312
3313 See :func:`_expression.update` for argument and usage information.
3314
3315 """
3316 return util.preloaded.sql_dml.Update(
3317 self,
3318 )
3319
3320 @util.preload_module("sqlalchemy.sql.dml")
3321 def delete(self) -> Delete:
3322 """Generate a :func:`_expression.delete` construct against this
3323 :class:`_expression.TableClause`.
3324
3325 E.g.::
3326
3327 table.delete().where(table.c.id == 7)
3328
3329 See :func:`_expression.delete` for argument and usage information.
3330
3331 """
3332 return util.preloaded.sql_dml.Delete(self)
3333
3334 @util.ro_non_memoized_property
3335 def _from_objects(self) -> List[FromClause]:
3336 return [self]
3337
3338
3339ForUpdateParameter = Union["ForUpdateArg", None, bool, Dict[str, Any]]
3340
3341
3342class ForUpdateArg(ClauseElement):
3343 _traverse_internals: _TraverseInternalsType = [
3344 ("of", InternalTraversal.dp_clauseelement_list),
3345 ("nowait", InternalTraversal.dp_boolean),
3346 ("read", InternalTraversal.dp_boolean),
3347 ("skip_locked", InternalTraversal.dp_boolean),
3348 ("key_share", InternalTraversal.dp_boolean),
3349 ]
3350
3351 of: Optional[Sequence[ClauseElement]]
3352 nowait: bool
3353 read: bool
3354 skip_locked: bool
3355
3356 @classmethod
3357 def _from_argument(
3358 cls, with_for_update: ForUpdateParameter
3359 ) -> Optional[ForUpdateArg]:
3360 if isinstance(with_for_update, ForUpdateArg):
3361 return with_for_update
3362 elif with_for_update in (None, False):
3363 return None
3364 elif with_for_update is True:
3365 return ForUpdateArg()
3366 else:
3367 return ForUpdateArg(**cast("Dict[str, Any]", with_for_update))
3368
3369 def __eq__(self, other: Any) -> bool:
3370 return (
3371 isinstance(other, ForUpdateArg)
3372 and other.nowait == self.nowait
3373 and other.read == self.read
3374 and other.skip_locked == self.skip_locked
3375 and other.key_share == self.key_share
3376 and other.of is self.of
3377 )
3378
3379 def __ne__(self, other: Any) -> bool:
3380 return not self.__eq__(other)
3381
3382 def __hash__(self) -> int:
3383 return id(self)
3384
3385 def __init__(
3386 self,
3387 *,
3388 nowait: bool = False,
3389 read: bool = False,
3390 of: Optional[_ForUpdateOfArgument] = None,
3391 skip_locked: bool = False,
3392 key_share: bool = False,
3393 ):
3394 """Represents arguments specified to
3395 :meth:`_expression.Select.for_update`.
3396
3397 """
3398
3399 self.nowait = nowait
3400 self.read = read
3401 self.skip_locked = skip_locked
3402 self.key_share = key_share
3403 if of is not None:
3404 self.of = [
3405 coercions.expect(roles.ColumnsClauseRole, elem)
3406 for elem in util.to_list(of)
3407 ]
3408 else:
3409 self.of = None
3410
3411
3412class Values(roles.InElementRole, HasCTE, Generative, LateralFromClause):
3413 """Represent a ``VALUES`` construct that can be used as a FROM element
3414 in a statement.
3415
3416 The :class:`_expression.Values` object is created from the
3417 :func:`_expression.values` function.
3418
3419 .. versionadded:: 1.4
3420
3421 """
3422
3423 __visit_name__ = "values"
3424
3425 _data: Tuple[Sequence[Tuple[Any, ...]], ...] = ()
3426 _column_args: Tuple[NamedColumn[Any], ...]
3427
3428 _unnamed: bool
3429 _traverse_internals: _TraverseInternalsType = [
3430 ("_column_args", InternalTraversal.dp_clauseelement_list),
3431 ("_data", InternalTraversal.dp_dml_multi_values),
3432 ("name", InternalTraversal.dp_string),
3433 ("literal_binds", InternalTraversal.dp_boolean),
3434 ] + HasCTE._has_ctes_traverse_internals
3435
3436 name_cte_columns = True
3437
3438 def __init__(
3439 self,
3440 *columns: _OnlyColumnArgument[Any],
3441 name: Optional[str] = None,
3442 literal_binds: bool = False,
3443 ):
3444 super().__init__()
3445 self._column_args = tuple(
3446 coercions.expect(roles.LabeledColumnExprRole, col)
3447 for col in columns
3448 )
3449
3450 if name is None:
3451 self._unnamed = True
3452 self.name = _anonymous_label.safe_construct(id(self), "anon")
3453 else:
3454 self._unnamed = False
3455 self.name = name
3456 self.literal_binds = literal_binds
3457 self.named_with_column = not self._unnamed
3458
3459 @property
3460 def _column_types(self) -> List[TypeEngine[Any]]:
3461 return [col.type for col in self._column_args]
3462
3463 @util.ro_non_memoized_property
3464 def _all_selected_columns(self) -> _SelectIterable:
3465 return self._column_args
3466
3467 @_generative
3468 def alias(self, name: Optional[str] = None, flat: bool = False) -> Self:
3469 """Return a new :class:`_expression.Values`
3470 construct that is a copy of this
3471 one with the given name.
3472
3473 This method is a VALUES-specific specialization of the
3474 :meth:`_expression.FromClause.alias` method.
3475
3476 .. seealso::
3477
3478 :ref:`tutorial_using_aliases`
3479
3480 :func:`_expression.alias`
3481
3482 """
3483 non_none_name: str
3484
3485 if name is None:
3486 non_none_name = _anonymous_label.safe_construct(id(self), "anon")
3487 else:
3488 non_none_name = name
3489
3490 self.name = non_none_name
3491 self.named_with_column = True
3492 self._unnamed = False
3493 return self
3494
3495 @_generative
3496 def lateral(self, name: Optional[str] = None) -> Self:
3497 """Return a new :class:`_expression.Values` with the lateral flag set,
3498 so that
3499 it renders as LATERAL.
3500
3501 .. seealso::
3502
3503 :func:`_expression.lateral`
3504
3505 """
3506 non_none_name: str
3507
3508 if name is None:
3509 non_none_name = self.name
3510 else:
3511 non_none_name = name
3512
3513 self._is_lateral = True
3514 self.name = non_none_name
3515 self._unnamed = False
3516 return self
3517
3518 @_generative
3519 def data(self, values: Sequence[Tuple[Any, ...]]) -> Self:
3520 """Return a new :class:`_expression.Values` construct,
3521 adding the given data to the data list.
3522
3523 E.g.::
3524
3525 my_values = my_values.data([(1, "value 1"), (2, "value2")])
3526
3527 :param values: a sequence (i.e. list) of tuples that map to the
3528 column expressions given in the :class:`_expression.Values`
3529 constructor.
3530
3531 """
3532
3533 self._data += (values,)
3534 return self
3535
3536 def scalar_values(self) -> ScalarValues:
3537 """Returns a scalar ``VALUES`` construct that can be used as a
3538 COLUMN element in a statement.
3539
3540 .. versionadded:: 2.0.0b4
3541
3542 """
3543 return ScalarValues(self._column_args, self._data, self.literal_binds)
3544
3545 def _populate_column_collection(
3546 self,
3547 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
3548 primary_key: ColumnSet,
3549 foreign_keys: Set[KeyedColumnElement[Any]],
3550 ) -> None:
3551 for c in self._column_args:
3552 if c.table is not None and c.table is not self:
3553 _, c = c._make_proxy(
3554 self, primary_key=primary_key, foreign_keys=foreign_keys
3555 )
3556 else:
3557 # if the column was used in other contexts, ensure
3558 # no memoizations of other FROM clauses.
3559 # see test_values.py -> test_auto_proxy_select_direct_col
3560 c._reset_memoizations()
3561 columns.add(c)
3562 c.table = self
3563
3564 @util.ro_non_memoized_property
3565 def _from_objects(self) -> List[FromClause]:
3566 return [self]
3567
3568
3569class ScalarValues(roles.InElementRole, GroupedElement, ColumnElement[Any]):
3570 """Represent a scalar ``VALUES`` construct that can be used as a
3571 COLUMN element in a statement.
3572
3573 The :class:`_expression.ScalarValues` object is created from the
3574 :meth:`_expression.Values.scalar_values` method. It's also
3575 automatically generated when a :class:`_expression.Values` is used in
3576 an ``IN`` or ``NOT IN`` condition.
3577
3578 .. versionadded:: 2.0.0b4
3579
3580 """
3581
3582 __visit_name__ = "scalar_values"
3583
3584 _traverse_internals: _TraverseInternalsType = [
3585 ("_column_args", InternalTraversal.dp_clauseelement_list),
3586 ("_data", InternalTraversal.dp_dml_multi_values),
3587 ("literal_binds", InternalTraversal.dp_boolean),
3588 ]
3589
3590 def __init__(
3591 self,
3592 columns: Sequence[NamedColumn[Any]],
3593 data: Tuple[Sequence[Tuple[Any, ...]], ...],
3594 literal_binds: bool,
3595 ):
3596 super().__init__()
3597 self._column_args = columns
3598 self._data = data
3599 self.literal_binds = literal_binds
3600
3601 @property
3602 def _column_types(self) -> List[TypeEngine[Any]]:
3603 return [col.type for col in self._column_args]
3604
3605 def __clause_element__(self) -> ScalarValues:
3606 return self
3607
3608 if TYPE_CHECKING:
3609
3610 def self_group(
3611 self, against: Optional[OperatorType] = None
3612 ) -> Self: ...
3613
3614 def _ungroup(self) -> ColumnElement[Any]: ...
3615
3616
3617class SelectBase(
3618 roles.SelectStatementRole,
3619 roles.DMLSelectRole,
3620 roles.CompoundElementRole,
3621 roles.InElementRole,
3622 HasCTE,
3623 SupportsCloneAnnotations,
3624 Selectable,
3625):
3626 """Base class for SELECT statements.
3627
3628
3629 This includes :class:`_expression.Select`,
3630 :class:`_expression.CompoundSelect` and
3631 :class:`_expression.TextualSelect`.
3632
3633
3634 """
3635
3636 _is_select_base = True
3637 is_select = True
3638
3639 _label_style: SelectLabelStyle = LABEL_STYLE_NONE
3640
3641 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
3642 self._reset_memoizations()
3643
3644 @util.ro_non_memoized_property
3645 def selected_columns(
3646 self,
3647 ) -> ColumnCollection[str, ColumnElement[Any]]:
3648 """A :class:`_expression.ColumnCollection`
3649 representing the columns that
3650 this SELECT statement or similar construct returns in its result set.
3651
3652 This collection differs from the :attr:`_expression.FromClause.columns`
3653 collection of a :class:`_expression.FromClause` in that the columns
3654 within this collection cannot be directly nested inside another SELECT
3655 statement; a subquery must be applied first which provides for the
3656 necessary parenthesization required by SQL.
3657
3658 .. note::
3659
3660 The :attr:`_sql.SelectBase.selected_columns` collection does not
3661 include expressions established in the columns clause using the
3662 :func:`_sql.text` construct; these are silently omitted from the
3663 collection. To use plain textual column expressions inside of a
3664 :class:`_sql.Select` construct, use the :func:`_sql.literal_column`
3665 construct.
3666
3667 .. seealso::
3668
3669 :attr:`_sql.Select.selected_columns`
3670
3671 .. versionadded:: 1.4
3672
3673 """
3674 raise NotImplementedError()
3675
3676 def _generate_fromclause_column_proxies(
3677 self,
3678 subquery: FromClause,
3679 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
3680 primary_key: ColumnSet,
3681 foreign_keys: Set[KeyedColumnElement[Any]],
3682 *,
3683 proxy_compound_columns: Optional[
3684 Iterable[Sequence[ColumnElement[Any]]]
3685 ] = None,
3686 ) -> None:
3687 raise NotImplementedError()
3688
3689 @util.ro_non_memoized_property
3690 def _all_selected_columns(self) -> _SelectIterable:
3691 """A sequence of expressions that correspond to what is rendered
3692 in the columns clause, including :class:`_sql.TextClause`
3693 constructs.
3694
3695 .. versionadded:: 1.4.12
3696
3697 .. seealso::
3698
3699 :attr:`_sql.SelectBase.exported_columns`
3700
3701 """
3702 raise NotImplementedError()
3703
3704 @property
3705 def exported_columns(
3706 self,
3707 ) -> ReadOnlyColumnCollection[str, ColumnElement[Any]]:
3708 """A :class:`_expression.ColumnCollection`
3709 that represents the "exported"
3710 columns of this :class:`_expression.Selectable`, not including
3711 :class:`_sql.TextClause` constructs.
3712
3713 The "exported" columns for a :class:`_expression.SelectBase`
3714 object are synonymous
3715 with the :attr:`_expression.SelectBase.selected_columns` collection.
3716
3717 .. versionadded:: 1.4
3718
3719 .. seealso::
3720
3721 :attr:`_expression.Select.exported_columns`
3722
3723 :attr:`_expression.Selectable.exported_columns`
3724
3725 :attr:`_expression.FromClause.exported_columns`
3726
3727
3728 """
3729 return self.selected_columns._as_readonly()
3730
3731 def get_label_style(self) -> SelectLabelStyle:
3732 """
3733 Retrieve the current label style.
3734
3735 Implemented by subclasses.
3736
3737 """
3738 raise NotImplementedError()
3739
3740 def set_label_style(self, style: SelectLabelStyle) -> Self:
3741 """Return a new selectable with the specified label style.
3742
3743 Implemented by subclasses.
3744
3745 """
3746
3747 raise NotImplementedError()
3748
3749 def _scalar_type(self) -> TypeEngine[Any]:
3750 raise NotImplementedError()
3751
3752 @util.deprecated(
3753 "1.4",
3754 "The :meth:`_expression.SelectBase.as_scalar` "
3755 "method is deprecated and will be "
3756 "removed in a future release. Please refer to "
3757 ":meth:`_expression.SelectBase.scalar_subquery`.",
3758 )
3759 def as_scalar(self) -> ScalarSelect[Any]:
3760 return self.scalar_subquery()
3761
3762 def exists(self) -> Exists:
3763 """Return an :class:`_sql.Exists` representation of this selectable,
3764 which can be used as a column expression.
3765
3766 The returned object is an instance of :class:`_sql.Exists`.
3767
3768 .. seealso::
3769
3770 :func:`_sql.exists`
3771
3772 :ref:`tutorial_exists` - in the :term:`2.0 style` tutorial.
3773
3774 .. versionadded:: 1.4
3775
3776 """
3777 return Exists(self)
3778
3779 def scalar_subquery(self) -> ScalarSelect[Any]:
3780 """Return a 'scalar' representation of this selectable, which can be
3781 used as a column expression.
3782
3783 The returned object is an instance of :class:`_sql.ScalarSelect`.
3784
3785 Typically, a select statement which has only one column in its columns
3786 clause is eligible to be used as a scalar expression. The scalar
3787 subquery can then be used in the WHERE clause or columns clause of
3788 an enclosing SELECT.
3789
3790 Note that the scalar subquery differentiates from the FROM-level
3791 subquery that can be produced using the
3792 :meth:`_expression.SelectBase.subquery`
3793 method.
3794
3795 .. versionchanged:: 1.4 - the ``.as_scalar()`` method was renamed to
3796 :meth:`_expression.SelectBase.scalar_subquery`.
3797
3798 .. seealso::
3799
3800 :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
3801
3802 """
3803 if self._label_style is not LABEL_STYLE_NONE:
3804 self = self.set_label_style(LABEL_STYLE_NONE)
3805
3806 return ScalarSelect(self)
3807
3808 def label(self, name: Optional[str]) -> Label[Any]:
3809 """Return a 'scalar' representation of this selectable, embedded as a
3810 subquery with a label.
3811
3812 .. seealso::
3813
3814 :meth:`_expression.SelectBase.scalar_subquery`.
3815
3816 """
3817 return self.scalar_subquery().label(name)
3818
3819 def lateral(self, name: Optional[str] = None) -> LateralFromClause:
3820 """Return a LATERAL alias of this :class:`_expression.Selectable`.
3821
3822 The return value is the :class:`_expression.Lateral` construct also
3823 provided by the top-level :func:`_expression.lateral` function.
3824
3825 .. seealso::
3826
3827 :ref:`tutorial_lateral_correlation` - overview of usage.
3828
3829 """
3830 return Lateral._factory(self, name)
3831
3832 def subquery(self, name: Optional[str] = None) -> Subquery:
3833 """Return a subquery of this :class:`_expression.SelectBase`.
3834
3835 A subquery is from a SQL perspective a parenthesized, named
3836 construct that can be placed in the FROM clause of another
3837 SELECT statement.
3838
3839 Given a SELECT statement such as::
3840
3841 stmt = select(table.c.id, table.c.name)
3842
3843 The above statement might look like:
3844
3845 .. sourcecode:: sql
3846
3847 SELECT table.id, table.name FROM table
3848
3849 The subquery form by itself renders the same way, however when
3850 embedded into the FROM clause of another SELECT statement, it becomes
3851 a named sub-element::
3852
3853 subq = stmt.subquery()
3854 new_stmt = select(subq)
3855
3856 The above renders as:
3857
3858 .. sourcecode:: sql
3859
3860 SELECT anon_1.id, anon_1.name
3861 FROM (SELECT table.id, table.name FROM table) AS anon_1
3862
3863 Historically, :meth:`_expression.SelectBase.subquery`
3864 is equivalent to calling
3865 the :meth:`_expression.FromClause.alias`
3866 method on a FROM object; however,
3867 as a :class:`_expression.SelectBase`
3868 object is not directly FROM object,
3869 the :meth:`_expression.SelectBase.subquery`
3870 method provides clearer semantics.
3871
3872 .. versionadded:: 1.4
3873
3874 """
3875
3876 return Subquery._construct(
3877 self._ensure_disambiguated_names(), name=name
3878 )
3879
3880 @util.preload_module("sqlalchemy.sql.ddl")
3881 def into(
3882 self,
3883 target: str,
3884 *,
3885 metadata: Optional["MetaData"] = None,
3886 schema: Optional[str] = None,
3887 temporary: bool = False,
3888 if_not_exists: bool = False,
3889 ) -> CreateTableAs:
3890 """Create a :class:`_schema.CreateTableAs` construct from this SELECT.
3891
3892 This method provides a convenient way to create a ``CREATE TABLE ...
3893 AS`` statement from a SELECT, as well as compound SELECTs like UNION.
3894 The new table will be created with columns matching the SELECT list.
3895
3896 Supported on all included backends, the construct emits
3897 ``CREATE TABLE...AS`` for all backends except SQL Server, which instead
3898 emits a ``SELECT..INTO`` statement.
3899
3900 e.g.::
3901
3902 from sqlalchemy import select
3903
3904 # Create a new table from a SELECT
3905 stmt = (
3906 select(users.c.id, users.c.name)
3907 .where(users.c.status == "active")
3908 .into("active_users")
3909 )
3910
3911 with engine.begin() as conn:
3912 conn.execute(stmt)
3913
3914 # With optional flags
3915 stmt = (
3916 select(users.c.id)
3917 .where(users.c.status == "inactive")
3918 .into("inactive_users", schema="analytics", if_not_exists=True)
3919 )
3920
3921 .. versionadded:: 2.1
3922
3923 :param target: Name of the table to create as a string. Must be
3924 unqualified; use the ``schema`` parameter for qualification.
3925
3926 :param metadata: :class:`_schema.MetaData`, optional
3927 If provided, the :class:`_schema.Table` object available via the
3928 :attr:`.CreateTableAs.table` attribute will be associated with this
3929 :class:`.MetaData`. Otherwise, a new, empty :class:`.MetaData`
3930 is created.
3931
3932 :param schema: Optional schema name for the new table.
3933
3934 :param temporary: If True, create a temporary table where supported
3935
3936 :param if_not_exists: If True, add IF NOT EXISTS clause where supported
3937
3938 :return: A :class:`_schema.CreateTableAs` construct.
3939
3940 .. seealso::
3941
3942 :ref:`metadata_create_table_as` - in :ref:`metadata_toplevel`
3943
3944 :class:`_schema.CreateTableAs`
3945
3946 """
3947 sql_ddl = util.preloaded.sql_ddl
3948
3949 return sql_ddl.CreateTableAs(
3950 self,
3951 target,
3952 metadata=metadata,
3953 schema=schema,
3954 temporary=temporary,
3955 if_not_exists=if_not_exists,
3956 )
3957
3958 def _ensure_disambiguated_names(self) -> Self:
3959 """Ensure that the names generated by this selectbase will be
3960 disambiguated in some way, if possible.
3961
3962 """
3963
3964 raise NotImplementedError()
3965
3966 def alias(
3967 self, name: Optional[str] = None, flat: bool = False
3968 ) -> Subquery:
3969 """Return a named subquery against this
3970 :class:`_expression.SelectBase`.
3971
3972 For a :class:`_expression.SelectBase` (as opposed to a
3973 :class:`_expression.FromClause`),
3974 this returns a :class:`.Subquery` object which behaves mostly the
3975 same as the :class:`_expression.Alias` object that is used with a
3976 :class:`_expression.FromClause`.
3977
3978 .. versionchanged:: 1.4 The :meth:`_expression.SelectBase.alias`
3979 method is now
3980 a synonym for the :meth:`_expression.SelectBase.subquery` method.
3981
3982 """
3983 return self.subquery(name=name)
3984
3985
3986_SB = TypeVar("_SB", bound=SelectBase)
3987
3988
3989class SelectStatementGrouping(GroupedElement, SelectBase, Generic[_SB]):
3990 """Represent a grouping of a :class:`_expression.SelectBase`.
3991
3992 This differs from :class:`.Subquery` in that we are still
3993 an "inner" SELECT statement, this is strictly for grouping inside of
3994 compound selects.
3995
3996 """
3997
3998 __visit_name__ = "select_statement_grouping"
3999 _traverse_internals: _TraverseInternalsType = [
4000 ("element", InternalTraversal.dp_clauseelement)
4001 ] + SupportsCloneAnnotations._clone_annotations_traverse_internals
4002
4003 _is_select_container = True
4004
4005 element: _SB
4006
4007 def __init__(self, element: _SB) -> None:
4008 self.element = cast(
4009 _SB, coercions.expect(roles.SelectStatementRole, element)
4010 )
4011
4012 def _ensure_disambiguated_names(self) -> SelectStatementGrouping[_SB]:
4013 new_element = self.element._ensure_disambiguated_names()
4014 if new_element is not self.element:
4015 return SelectStatementGrouping(new_element)
4016 else:
4017 return self
4018
4019 def get_label_style(self) -> SelectLabelStyle:
4020 return self.element.get_label_style()
4021
4022 def set_label_style(
4023 self, label_style: SelectLabelStyle
4024 ) -> SelectStatementGrouping[_SB]:
4025 return SelectStatementGrouping(
4026 self.element.set_label_style(label_style)
4027 )
4028
4029 @property
4030 def select_statement(self) -> _SB:
4031 return self.element
4032
4033 def self_group(self, against: Optional[OperatorType] = None) -> Self:
4034 return self
4035
4036 if TYPE_CHECKING:
4037
4038 def _ungroup(self) -> _SB: ...
4039
4040 # def _generate_columns_plus_names(
4041 # self, anon_for_dupe_key: bool
4042 # ) -> List[Tuple[str, str, str, ColumnElement[Any], bool]]:
4043 # return self.element._generate_columns_plus_names(anon_for_dupe_key)
4044
4045 def _generate_fromclause_column_proxies(
4046 self,
4047 subquery: FromClause,
4048 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
4049 primary_key: ColumnSet,
4050 foreign_keys: Set[KeyedColumnElement[Any]],
4051 *,
4052 proxy_compound_columns: Optional[
4053 Iterable[Sequence[ColumnElement[Any]]]
4054 ] = None,
4055 ) -> None:
4056 self.element._generate_fromclause_column_proxies(
4057 subquery,
4058 columns,
4059 proxy_compound_columns=proxy_compound_columns,
4060 primary_key=primary_key,
4061 foreign_keys=foreign_keys,
4062 )
4063
4064 @util.ro_non_memoized_property
4065 def _all_selected_columns(self) -> _SelectIterable:
4066 return self.element._all_selected_columns
4067
4068 @util.ro_non_memoized_property
4069 def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]:
4070 """A :class:`_expression.ColumnCollection`
4071 representing the columns that
4072 the embedded SELECT statement returns in its result set, not including
4073 :class:`_sql.TextClause` constructs.
4074
4075 .. versionadded:: 1.4
4076
4077 .. seealso::
4078
4079 :attr:`_sql.Select.selected_columns`
4080
4081 """
4082 return self.element.selected_columns
4083
4084 @util.ro_non_memoized_property
4085 def _from_objects(self) -> List[FromClause]:
4086 return self.element._from_objects
4087
4088 def _scalar_type(self) -> TypeEngine[Any]:
4089 return self.element._scalar_type()
4090
4091 def add_cte(self, *ctes: CTE, nest_here: bool = False) -> Self:
4092 # SelectStatementGrouping not generative: has no attribute '_generate'
4093 raise NotImplementedError
4094
4095
4096class GenerativeSelect(DialectKWArgs, SelectBase, Generative):
4097 """Base class for SELECT statements where additional elements can be
4098 added.
4099
4100 This serves as the base for :class:`_expression.Select` and
4101 :class:`_expression.CompoundSelect`
4102 where elements such as ORDER BY, GROUP BY can be added and column
4103 rendering can be controlled. Compare to
4104 :class:`_expression.TextualSelect`, which,
4105 while it subclasses :class:`_expression.SelectBase`
4106 and is also a SELECT construct,
4107 represents a fixed textual string which cannot be altered at this level,
4108 only wrapped as a subquery.
4109
4110 """
4111
4112 _order_by_clauses: Tuple[ColumnElement[Any], ...] = ()
4113 _group_by_clauses: Tuple[ColumnElement[Any], ...] = ()
4114 _limit_clause: Optional[ColumnElement[Any]] = None
4115 _offset_clause: Optional[ColumnElement[Any]] = None
4116 _fetch_clause: Optional[ColumnElement[Any]] = None
4117 _fetch_clause_options: Optional[Dict[str, bool]] = None
4118 _for_update_arg: Optional[ForUpdateArg] = None
4119
4120 def __init__(self, _label_style: SelectLabelStyle = LABEL_STYLE_DEFAULT):
4121 self._label_style = _label_style
4122
4123 @_generative
4124 def with_for_update(
4125 self,
4126 *,
4127 nowait: bool = False,
4128 read: bool = False,
4129 of: Optional[_ForUpdateOfArgument] = None,
4130 skip_locked: bool = False,
4131 key_share: bool = False,
4132 ) -> Self:
4133 """Specify a ``FOR UPDATE`` clause for this
4134 :class:`_expression.GenerativeSelect`.
4135
4136 E.g.::
4137
4138 stmt = select(table).with_for_update(nowait=True)
4139
4140 On a database like PostgreSQL or Oracle Database, the above would
4141 render a statement like:
4142
4143 .. sourcecode:: sql
4144
4145 SELECT table.a, table.b FROM table FOR UPDATE NOWAIT
4146
4147 on other backends, the ``nowait`` option is ignored and instead
4148 would produce:
4149
4150 .. sourcecode:: sql
4151
4152 SELECT table.a, table.b FROM table FOR UPDATE
4153
4154 When called with no arguments, the statement will render with
4155 the suffix ``FOR UPDATE``. Additional arguments can then be
4156 provided which allow for common database-specific
4157 variants.
4158
4159 :param nowait: boolean; will render ``FOR UPDATE NOWAIT`` on Oracle
4160 Database and PostgreSQL dialects.
4161
4162 :param read: boolean; will render ``LOCK IN SHARE MODE`` on MySQL,
4163 ``FOR SHARE`` on PostgreSQL. On PostgreSQL, when combined with
4164 ``nowait``, will render ``FOR SHARE NOWAIT``.
4165
4166 :param of: SQL expression or list of SQL expression elements,
4167 (typically :class:`_schema.Column` objects or a compatible expression,
4168 for some backends may also be a table expression) which will render
4169 into a ``FOR UPDATE OF`` clause; supported by PostgreSQL, Oracle
4170 Database, some MySQL versions and possibly others. May render as a
4171 table or as a column depending on backend.
4172
4173 :param skip_locked: boolean, will render ``FOR UPDATE SKIP LOCKED`` on
4174 Oracle Database and PostgreSQL dialects or ``FOR SHARE SKIP LOCKED``
4175 if ``read=True`` is also specified.
4176
4177 :param key_share: boolean, will render ``FOR NO KEY UPDATE``,
4178 or if combined with ``read=True`` will render ``FOR KEY SHARE``,
4179 on the PostgreSQL dialect.
4180
4181 """
4182 self._for_update_arg = ForUpdateArg(
4183 nowait=nowait,
4184 read=read,
4185 of=of,
4186 skip_locked=skip_locked,
4187 key_share=key_share,
4188 )
4189 return self
4190
4191 def get_label_style(self) -> SelectLabelStyle:
4192 """
4193 Retrieve the current label style.
4194
4195 .. versionadded:: 1.4
4196
4197 """
4198 return self._label_style
4199
4200 def set_label_style(self, style: SelectLabelStyle) -> Self:
4201 """Return a new selectable with the specified label style.
4202
4203 There are three "label styles" available,
4204 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`,
4205 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`, and
4206 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_NONE`. The default style is
4207 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`.
4208
4209 In modern SQLAlchemy, there is not generally a need to change the
4210 labeling style, as per-expression labels are more effectively used by
4211 making use of the :meth:`_sql.ColumnElement.label` method. In past
4212 versions, :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` was used to
4213 disambiguate same-named columns from different tables, aliases, or
4214 subqueries; the newer :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` now
4215 applies labels only to names that conflict with an existing name so
4216 that the impact of this labeling is minimal.
4217
4218 The rationale for disambiguation is mostly so that all column
4219 expressions are available from a given :attr:`_sql.FromClause.c`
4220 collection when a subquery is created.
4221
4222 .. versionadded:: 1.4 - the
4223 :meth:`_sql.GenerativeSelect.set_label_style` method replaces the
4224 previous combination of ``.apply_labels()``, ``.with_labels()`` and
4225 ``use_labels=True`` methods and/or parameters.
4226
4227 .. seealso::
4228
4229 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`
4230
4231 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`
4232
4233 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_NONE`
4234
4235 :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DEFAULT`
4236
4237 """
4238 if self._label_style is not style:
4239 self = self._generate()
4240 self._label_style = style
4241 return self
4242
4243 @property
4244 def _group_by_clause(self) -> ClauseList:
4245 """ClauseList access to group_by_clauses for legacy dialects"""
4246 return ClauseList._construct_raw(
4247 operators.comma_op, self._group_by_clauses
4248 )
4249
4250 @property
4251 def _order_by_clause(self) -> ClauseList:
4252 """ClauseList access to order_by_clauses for legacy dialects"""
4253 return ClauseList._construct_raw(
4254 operators.comma_op, self._order_by_clauses
4255 )
4256
4257 def _offset_or_limit_clause(
4258 self,
4259 element: _LimitOffsetType,
4260 name: Optional[str] = None,
4261 type_: Optional[_TypeEngineArgument[int]] = None,
4262 ) -> ColumnElement[Any]:
4263 """Convert the given value to an "offset or limit" clause.
4264
4265 This handles incoming integers and converts to an expression; if
4266 an expression is already given, it is passed through.
4267
4268 """
4269 return coercions.expect(
4270 roles.LimitOffsetRole, element, name=name, type_=type_
4271 )
4272
4273 @overload
4274 def _offset_or_limit_clause_asint(
4275 self, clause: ColumnElement[Any], attrname: str
4276 ) -> NoReturn: ...
4277
4278 @overload
4279 def _offset_or_limit_clause_asint(
4280 self, clause: Optional[_OffsetLimitParam], attrname: str
4281 ) -> Optional[int]: ...
4282
4283 def _offset_or_limit_clause_asint(
4284 self, clause: Optional[ColumnElement[Any]], attrname: str
4285 ) -> Union[NoReturn, Optional[int]]:
4286 """Convert the "offset or limit" clause of a select construct to an
4287 integer.
4288
4289 This is only possible if the value is stored as a simple bound
4290 parameter. Otherwise, a compilation error is raised.
4291
4292 """
4293 if clause is None:
4294 return None
4295 try:
4296 value = clause._limit_offset_value
4297 except AttributeError as err:
4298 raise exc.CompileError(
4299 "This SELECT structure does not use a simple "
4300 "integer value for %s" % attrname
4301 ) from err
4302 else:
4303 return util.asint(value)
4304
4305 @property
4306 def _limit(self) -> Optional[int]:
4307 """Get an integer value for the limit. This should only be used
4308 by code that cannot support a limit as a BindParameter or
4309 other custom clause as it will throw an exception if the limit
4310 isn't currently set to an integer.
4311
4312 """
4313 return self._offset_or_limit_clause_asint(self._limit_clause, "limit")
4314
4315 def _simple_int_clause(self, clause: ClauseElement) -> bool:
4316 """True if the clause is a simple integer, False
4317 if it is not present or is a SQL expression.
4318 """
4319 return isinstance(clause, _OffsetLimitParam)
4320
4321 @property
4322 def _offset(self) -> Optional[int]:
4323 """Get an integer value for the offset. This should only be used
4324 by code that cannot support an offset as a BindParameter or
4325 other custom clause as it will throw an exception if the
4326 offset isn't currently set to an integer.
4327
4328 """
4329 return self._offset_or_limit_clause_asint(
4330 self._offset_clause, "offset"
4331 )
4332
4333 @property
4334 def _has_row_limiting_clause(self) -> bool:
4335 return (
4336 self._limit_clause is not None
4337 or self._offset_clause is not None
4338 or self._fetch_clause is not None
4339 )
4340
4341 @_generative
4342 def limit(self, limit: _LimitOffsetType) -> Self:
4343 """Return a new selectable with the given LIMIT criterion
4344 applied.
4345
4346 This is a numerical value which usually renders as a ``LIMIT``
4347 expression in the resulting select. Backends that don't
4348 support ``LIMIT`` will attempt to provide similar
4349 functionality.
4350
4351 .. note::
4352
4353 The :meth:`_sql.GenerativeSelect.limit` method will replace
4354 any clause applied with :meth:`_sql.GenerativeSelect.fetch`.
4355
4356 :param limit: an integer LIMIT parameter, or a SQL expression
4357 that provides an integer result. Pass ``None`` to reset it.
4358
4359 .. seealso::
4360
4361 :meth:`_sql.GenerativeSelect.fetch`
4362
4363 :meth:`_sql.GenerativeSelect.offset`
4364
4365 """
4366
4367 self._fetch_clause = self._fetch_clause_options = None
4368 self._limit_clause = self._offset_or_limit_clause(limit)
4369 return self
4370
4371 @_generative
4372 def fetch(
4373 self,
4374 count: _LimitOffsetType,
4375 with_ties: bool = False,
4376 percent: bool = False,
4377 **dialect_kw: Any,
4378 ) -> Self:
4379 r"""Return a new selectable with the given FETCH FIRST criterion
4380 applied.
4381
4382 This is a numeric value which usually renders as ``FETCH {FIRST | NEXT}
4383 [ count ] {ROW | ROWS} {ONLY | WITH TIES}`` expression in the resulting
4384 select. This functionality is is currently implemented for Oracle
4385 Database, PostgreSQL, MSSQL.
4386
4387 Use :meth:`_sql.GenerativeSelect.offset` to specify the offset.
4388
4389 .. note::
4390
4391 The :meth:`_sql.GenerativeSelect.fetch` method will replace
4392 any clause applied with :meth:`_sql.GenerativeSelect.limit`.
4393
4394 .. versionadded:: 1.4
4395
4396 :param count: an integer COUNT parameter, or a SQL expression
4397 that provides an integer result. When ``percent=True`` this will
4398 represent the percentage of rows to return, not the absolute value.
4399 Pass ``None`` to reset it.
4400
4401 :param with_ties: When ``True``, the WITH TIES option is used
4402 to return any additional rows that tie for the last place in the
4403 result set according to the ``ORDER BY`` clause. The
4404 ``ORDER BY`` may be mandatory in this case. Defaults to ``False``
4405
4406 :param percent: When ``True``, ``count`` represents the percentage
4407 of the total number of selected rows to return. Defaults to ``False``
4408
4409 :param \**dialect_kw: Additional dialect-specific keyword arguments
4410 may be accepted by dialects.
4411
4412 .. versionadded:: 2.0.41
4413
4414 .. seealso::
4415
4416 :meth:`_sql.GenerativeSelect.limit`
4417
4418 :meth:`_sql.GenerativeSelect.offset`
4419
4420 """
4421 self._validate_dialect_kwargs(dialect_kw)
4422 self._limit_clause = None
4423 if count is None:
4424 self._fetch_clause = self._fetch_clause_options = None
4425 else:
4426 self._fetch_clause = self._offset_or_limit_clause(count)
4427 self._fetch_clause_options = {
4428 "with_ties": with_ties,
4429 "percent": percent,
4430 }
4431 return self
4432
4433 @_generative
4434 def offset(self, offset: _LimitOffsetType) -> Self:
4435 """Return a new selectable with the given OFFSET criterion
4436 applied.
4437
4438
4439 This is a numeric value which usually renders as an ``OFFSET``
4440 expression in the resulting select. Backends that don't
4441 support ``OFFSET`` will attempt to provide similar
4442 functionality.
4443
4444 :param offset: an integer OFFSET parameter, or a SQL expression
4445 that provides an integer result. Pass ``None`` to reset it.
4446
4447 .. seealso::
4448
4449 :meth:`_sql.GenerativeSelect.limit`
4450
4451 :meth:`_sql.GenerativeSelect.fetch`
4452
4453 """
4454
4455 self._offset_clause = self._offset_or_limit_clause(offset)
4456 return self
4457
4458 @_generative
4459 @util.preload_module("sqlalchemy.sql.util")
4460 def slice(
4461 self,
4462 start: int,
4463 stop: int,
4464 ) -> Self:
4465 """Apply LIMIT / OFFSET to this statement based on a slice.
4466
4467 The start and stop indices behave like the argument to Python's
4468 built-in :func:`range` function. This method provides an
4469 alternative to using ``LIMIT``/``OFFSET`` to get a slice of the
4470 query.
4471
4472 For example, ::
4473
4474 stmt = select(User).order_by(User.id).slice(1, 3)
4475
4476 renders as
4477
4478 .. sourcecode:: sql
4479
4480 SELECT users.id AS users_id,
4481 users.name AS users_name
4482 FROM users ORDER BY users.id
4483 LIMIT ? OFFSET ?
4484 (2, 1)
4485
4486 .. note::
4487
4488 The :meth:`_sql.GenerativeSelect.slice` method will replace
4489 any clause applied with :meth:`_sql.GenerativeSelect.fetch`.
4490
4491 .. versionadded:: 1.4 Added the :meth:`_sql.GenerativeSelect.slice`
4492 method generalized from the ORM.
4493
4494 .. seealso::
4495
4496 :meth:`_sql.GenerativeSelect.limit`
4497
4498 :meth:`_sql.GenerativeSelect.offset`
4499
4500 :meth:`_sql.GenerativeSelect.fetch`
4501
4502 """
4503 sql_util = util.preloaded.sql_util
4504 self._fetch_clause = self._fetch_clause_options = None
4505 self._limit_clause, self._offset_clause = sql_util._make_slice(
4506 self._limit_clause, self._offset_clause, start, stop
4507 )
4508 return self
4509
4510 @_generative
4511 def order_by(
4512 self,
4513 __first: Union[
4514 Literal[None, _NoArg.NO_ARG],
4515 _ColumnExpressionOrStrLabelArgument[Any],
4516 roles.OrderByRole,
4517 ] = _NoArg.NO_ARG,
4518 /,
4519 *clauses: Union[
4520 _ColumnExpressionOrStrLabelArgument[Any], roles.OrderByRole
4521 ],
4522 ) -> Self:
4523 r"""Return a new selectable with the given list of ORDER BY
4524 criteria applied.
4525
4526 e.g.::
4527
4528 stmt = select(table).order_by(table.c.id, table.c.name)
4529
4530 Calling this method multiple times is equivalent to calling it once
4531 with all the clauses concatenated. All existing ORDER BY criteria may
4532 be cancelled by passing ``None`` by itself. New ORDER BY criteria may
4533 then be added by invoking :meth:`_orm.Query.order_by` again, e.g.::
4534
4535 # will erase all ORDER BY and ORDER BY new_col alone
4536 stmt = stmt.order_by(None).order_by(new_col)
4537
4538 :param \*clauses: a series of :class:`_expression.ColumnElement`
4539 constructs which will be used to generate an ORDER BY clause.
4540
4541 Alternatively, an individual entry may also be the string name of a
4542 label located elsewhere in the columns clause of the statement which
4543 will be matched and rendered in a backend-specific way based on
4544 context; see :ref:`tutorial_order_by_label` for background on string
4545 label matching in ORDER BY and GROUP BY expressions.
4546
4547 .. seealso::
4548
4549 :ref:`tutorial_order_by` - in the :ref:`unified_tutorial`
4550
4551 :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial`
4552
4553 """
4554
4555 if not clauses and __first is None:
4556 self._order_by_clauses = ()
4557 elif __first is not _NoArg.NO_ARG:
4558 self._order_by_clauses += tuple(
4559 coercions.expect(
4560 roles.OrderByRole, clause, apply_propagate_attrs=self
4561 )
4562 for clause in (__first,) + clauses
4563 )
4564 return self
4565
4566 @_generative
4567 def group_by(
4568 self,
4569 __first: Union[
4570 Literal[None, _NoArg.NO_ARG],
4571 _ColumnExpressionOrStrLabelArgument[Any],
4572 ] = _NoArg.NO_ARG,
4573 /,
4574 *clauses: _ColumnExpressionOrStrLabelArgument[Any],
4575 ) -> Self:
4576 r"""Return a new selectable with the given list of GROUP BY
4577 criterion applied.
4578
4579 All existing GROUP BY settings can be suppressed by passing ``None``.
4580
4581 e.g.::
4582
4583 stmt = select(table.c.name, func.max(table.c.stat)).group_by(table.c.name)
4584
4585 :param \*clauses: a series of :class:`_expression.ColumnElement`
4586 constructs which will be used to generate an GROUP BY clause.
4587
4588 Alternatively, an individual entry may also be the string name of a
4589 label located elsewhere in the columns clause of the statement which
4590 will be matched and rendered in a backend-specific way based on
4591 context; see :ref:`tutorial_order_by_label` for background on string
4592 label matching in ORDER BY and GROUP BY expressions.
4593
4594 .. seealso::
4595
4596 :ref:`tutorial_group_by_w_aggregates` - in the
4597 :ref:`unified_tutorial`
4598
4599 :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial`
4600
4601 """ # noqa: E501
4602
4603 if not clauses and __first is None:
4604 self._group_by_clauses = ()
4605 elif __first is not _NoArg.NO_ARG:
4606 self._group_by_clauses += tuple(
4607 coercions.expect(
4608 roles.GroupByRole, clause, apply_propagate_attrs=self
4609 )
4610 for clause in (__first,) + clauses
4611 )
4612 return self
4613
4614
4615@CompileState.plugin_for("default", "compound_select")
4616class CompoundSelectState(CompileState):
4617 @util.memoized_property
4618 def _label_resolve_dict(
4619 self,
4620 ) -> Tuple[
4621 Dict[str, ColumnElement[Any]],
4622 Dict[str, ColumnElement[Any]],
4623 Dict[str, ColumnElement[Any]],
4624 ]:
4625 # TODO: this is hacky and slow
4626 hacky_subquery = self.statement.subquery()
4627 hacky_subquery.named_with_column = False
4628 d = {c.key: c for c in hacky_subquery.c}
4629 return d, d, d
4630
4631
4632class _CompoundSelectKeyword(Enum):
4633 UNION = "UNION"
4634 UNION_ALL = "UNION ALL"
4635 EXCEPT = "EXCEPT"
4636 EXCEPT_ALL = "EXCEPT ALL"
4637 INTERSECT = "INTERSECT"
4638 INTERSECT_ALL = "INTERSECT ALL"
4639
4640
4641class CompoundSelect(
4642 HasCompileState, GenerativeSelect, TypedReturnsRows[Unpack[_Ts]]
4643):
4644 """Forms the basis of ``UNION``, ``UNION ALL``, and other
4645 SELECT-based set operations.
4646
4647
4648 .. seealso::
4649
4650 :func:`_expression.union`
4651
4652 :func:`_expression.union_all`
4653
4654 :func:`_expression.intersect`
4655
4656 :func:`_expression.intersect_all`
4657
4658 :func:`_expression.except`
4659
4660 :func:`_expression.except_all`
4661
4662 """
4663
4664 __visit_name__ = "compound_select"
4665
4666 _traverse_internals: _TraverseInternalsType = (
4667 [
4668 ("selects", InternalTraversal.dp_clauseelement_list),
4669 ("_limit_clause", InternalTraversal.dp_clauseelement),
4670 ("_offset_clause", InternalTraversal.dp_clauseelement),
4671 ("_fetch_clause", InternalTraversal.dp_clauseelement),
4672 ("_fetch_clause_options", InternalTraversal.dp_plain_dict),
4673 ("_order_by_clauses", InternalTraversal.dp_clauseelement_list),
4674 ("_group_by_clauses", InternalTraversal.dp_clauseelement_list),
4675 ("_for_update_arg", InternalTraversal.dp_clauseelement),
4676 ("keyword", InternalTraversal.dp_string),
4677 ]
4678 + SupportsCloneAnnotations._clone_annotations_traverse_internals
4679 + HasCTE._has_ctes_traverse_internals
4680 + DialectKWArgs._dialect_kwargs_traverse_internals
4681 + ExecutableStatement._executable_traverse_internals
4682 )
4683
4684 selects: List[SelectBase]
4685
4686 _is_from_container = True
4687 _auto_correlate = False
4688
4689 def __init__(
4690 self,
4691 keyword: _CompoundSelectKeyword,
4692 *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]],
4693 ):
4694 self.keyword = keyword
4695 self.selects = [
4696 coercions.expect(
4697 roles.CompoundElementRole, s, apply_propagate_attrs=self
4698 ).self_group(against=self)
4699 for s in selects
4700 ]
4701
4702 GenerativeSelect.__init__(self)
4703
4704 @classmethod
4705 def _create_union(
4706 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4707 ) -> CompoundSelect[Unpack[_Ts]]:
4708 return CompoundSelect(_CompoundSelectKeyword.UNION, *selects)
4709
4710 @classmethod
4711 def _create_union_all(
4712 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4713 ) -> CompoundSelect[Unpack[_Ts]]:
4714 return CompoundSelect(_CompoundSelectKeyword.UNION_ALL, *selects)
4715
4716 @classmethod
4717 def _create_except(
4718 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4719 ) -> CompoundSelect[Unpack[_Ts]]:
4720 return CompoundSelect(_CompoundSelectKeyword.EXCEPT, *selects)
4721
4722 @classmethod
4723 def _create_except_all(
4724 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4725 ) -> CompoundSelect[Unpack[_Ts]]:
4726 return CompoundSelect(_CompoundSelectKeyword.EXCEPT_ALL, *selects)
4727
4728 @classmethod
4729 def _create_intersect(
4730 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4731 ) -> CompoundSelect[Unpack[_Ts]]:
4732 return CompoundSelect(_CompoundSelectKeyword.INTERSECT, *selects)
4733
4734 @classmethod
4735 def _create_intersect_all(
4736 cls, *selects: _SelectStatementForCompoundArgument[Unpack[_Ts]]
4737 ) -> CompoundSelect[Unpack[_Ts]]:
4738 return CompoundSelect(_CompoundSelectKeyword.INTERSECT_ALL, *selects)
4739
4740 def _scalar_type(self) -> TypeEngine[Any]:
4741 return self.selects[0]._scalar_type()
4742
4743 def self_group(
4744 self, against: Optional[OperatorType] = None
4745 ) -> GroupedElement:
4746 return SelectStatementGrouping(self)
4747
4748 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
4749 for s in self.selects:
4750 if s.is_derived_from(fromclause):
4751 return True
4752 return False
4753
4754 def set_label_style(self, style: SelectLabelStyle) -> Self:
4755 if self._label_style is not style:
4756 self = self._generate()
4757 select_0 = self.selects[0].set_label_style(style)
4758 self.selects = [select_0] + self.selects[1:]
4759
4760 return self
4761
4762 def _ensure_disambiguated_names(self) -> Self:
4763 new_select = self.selects[0]._ensure_disambiguated_names()
4764 if new_select is not self.selects[0]:
4765 self = self._generate()
4766 self.selects = [new_select] + self.selects[1:]
4767
4768 return self
4769
4770 def _generate_fromclause_column_proxies(
4771 self,
4772 subquery: FromClause,
4773 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
4774 primary_key: ColumnSet,
4775 foreign_keys: Set[KeyedColumnElement[Any]],
4776 *,
4777 proxy_compound_columns: Optional[
4778 Iterable[Sequence[ColumnElement[Any]]]
4779 ] = None,
4780 ) -> None:
4781 # this is a slightly hacky thing - the union exports a
4782 # column that resembles just that of the *first* selectable.
4783 # to get at a "composite" column, particularly foreign keys,
4784 # you have to dig through the proxies collection which we
4785 # generate below.
4786 select_0 = self.selects[0]
4787
4788 if self._label_style is not LABEL_STYLE_DEFAULT:
4789 select_0 = select_0.set_label_style(self._label_style)
4790
4791 # hand-construct the "_proxies" collection to include all
4792 # derived columns place a 'weight' annotation corresponding
4793 # to how low in the list of select()s the column occurs, so
4794 # that the corresponding_column() operation can resolve
4795 # conflicts
4796 extra_col_iterator = zip(
4797 *[
4798 [
4799 c._annotate(dd)
4800 for c in stmt._all_selected_columns
4801 if is_column_element(c)
4802 ]
4803 for dd, stmt in [
4804 ({"weight": i + 1}, stmt)
4805 for i, stmt in enumerate(self.selects)
4806 ]
4807 ]
4808 )
4809
4810 # the incoming proxy_compound_columns can be present also if this is
4811 # a compound embedded in a compound. it's probably more appropriate
4812 # that we generate new weights local to this nested compound, though
4813 # i haven't tried to think what it means for compound nested in
4814 # compound
4815 select_0._generate_fromclause_column_proxies(
4816 subquery,
4817 columns,
4818 proxy_compound_columns=extra_col_iterator,
4819 primary_key=primary_key,
4820 foreign_keys=foreign_keys,
4821 )
4822
4823 def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None:
4824 super()._refresh_for_new_column(column)
4825 for select in self.selects:
4826 select._refresh_for_new_column(column)
4827
4828 @util.ro_non_memoized_property
4829 def _all_selected_columns(self) -> _SelectIterable:
4830 return self.selects[0]._all_selected_columns
4831
4832 @util.ro_non_memoized_property
4833 def selected_columns(
4834 self,
4835 ) -> ColumnCollection[str, ColumnElement[Any]]:
4836 """A :class:`_expression.ColumnCollection`
4837 representing the columns that
4838 this SELECT statement or similar construct returns in its result set,
4839 not including :class:`_sql.TextClause` constructs.
4840
4841 For a :class:`_expression.CompoundSelect`, the
4842 :attr:`_expression.CompoundSelect.selected_columns`
4843 attribute returns the selected
4844 columns of the first SELECT statement contained within the series of
4845 statements within the set operation.
4846
4847 .. seealso::
4848
4849 :attr:`_sql.Select.selected_columns`
4850
4851 .. versionadded:: 1.4
4852
4853 """
4854 return self.selects[0].selected_columns
4855
4856
4857# backwards compat
4858for elem in _CompoundSelectKeyword:
4859 setattr(CompoundSelect, elem.name, elem)
4860
4861
4862@CompileState.plugin_for("default", "select")
4863class SelectState(util.MemoizedSlots, CompileState):
4864 __slots__ = (
4865 "from_clauses",
4866 "froms",
4867 "columns_plus_names",
4868 "_label_resolve_dict",
4869 )
4870
4871 if TYPE_CHECKING:
4872 default_select_compile_options: CacheableOptions
4873 else:
4874
4875 class default_select_compile_options(CacheableOptions):
4876 _cache_key_traversal = []
4877
4878 if TYPE_CHECKING:
4879
4880 @classmethod
4881 def get_plugin_class(
4882 cls, statement: Executable
4883 ) -> Type[SelectState]: ...
4884
4885 def __init__(
4886 self,
4887 statement: Select[Unpack[TupleAny]],
4888 compiler: SQLCompiler,
4889 **kw: Any,
4890 ):
4891 self.statement = statement
4892 self.from_clauses = statement._from_obj
4893
4894 for memoized_entities in statement._memoized_select_entities:
4895 self._setup_joins(
4896 memoized_entities._setup_joins, memoized_entities._raw_columns
4897 )
4898
4899 if statement._setup_joins:
4900 self._setup_joins(statement._setup_joins, statement._raw_columns)
4901
4902 self.froms = self._get_froms(statement)
4903
4904 self.columns_plus_names = statement._generate_columns_plus_names(True)
4905
4906 @classmethod
4907 def _plugin_not_implemented(cls) -> NoReturn:
4908 raise NotImplementedError(
4909 "The default SELECT construct without plugins does not "
4910 "implement this method."
4911 )
4912
4913 @classmethod
4914 def get_column_descriptions(
4915 cls, statement: Select[Unpack[TupleAny]]
4916 ) -> List[Dict[str, Any]]:
4917 return [
4918 {
4919 "name": name,
4920 "type": element.type,
4921 "expr": element,
4922 }
4923 for _, name, _, element, _ in (
4924 statement._generate_columns_plus_names(False)
4925 )
4926 ]
4927
4928 @classmethod
4929 def from_statement(
4930 cls,
4931 statement: Select[Unpack[TupleAny]],
4932 from_statement: roles.ReturnsRowsRole,
4933 ) -> ExecutableReturnsRows:
4934 cls._plugin_not_implemented()
4935
4936 @classmethod
4937 def get_columns_clause_froms(
4938 cls, statement: Select[Unpack[TupleAny]]
4939 ) -> List[FromClause]:
4940 return cls._normalize_froms(
4941 itertools.chain.from_iterable(
4942 element._from_objects for element in statement._raw_columns
4943 )
4944 )
4945
4946 @classmethod
4947 def _column_naming_convention(
4948 cls, label_style: SelectLabelStyle
4949 ) -> _LabelConventionCallable:
4950 table_qualified = label_style is LABEL_STYLE_TABLENAME_PLUS_COL
4951
4952 dedupe = label_style is not LABEL_STYLE_NONE
4953
4954 pa = prefix_anon_map()
4955 names = set()
4956
4957 def go(
4958 c: Union[ColumnElement[Any], AbstractTextClause],
4959 col_name: Optional[str] = None,
4960 ) -> Optional[str]:
4961 if is_text_clause(c):
4962 return None
4963 elif TYPE_CHECKING:
4964 assert is_column_element(c)
4965
4966 if not dedupe:
4967 name = c._proxy_key
4968 if name is None:
4969 name = "_no_label"
4970 return name
4971
4972 name = c._tq_key_label if table_qualified else c._proxy_key
4973
4974 if name is None:
4975 name = "_no_label"
4976 if name in names:
4977 return c._anon_label(name) % pa
4978 else:
4979 names.add(name)
4980 return name
4981
4982 elif name in names:
4983 return (
4984 c._anon_tq_key_label % pa
4985 if table_qualified
4986 else c._anon_key_label % pa
4987 )
4988 else:
4989 names.add(name)
4990 return name
4991
4992 return go
4993
4994 def _get_froms(
4995 self, statement: Select[Unpack[TupleAny]]
4996 ) -> List[FromClause]:
4997 ambiguous_table_name_map: _AmbiguousTableNameMap
4998 self._ambiguous_table_name_map = ambiguous_table_name_map = {}
4999
5000 return self._normalize_froms(
5001 itertools.chain(
5002 self.from_clauses,
5003 itertools.chain.from_iterable(
5004 [
5005 element._from_objects
5006 for element in statement._raw_columns
5007 ]
5008 ),
5009 itertools.chain.from_iterable(
5010 [
5011 element._from_objects
5012 for element in statement._where_criteria
5013 ]
5014 ),
5015 ),
5016 check_statement=statement,
5017 ambiguous_table_name_map=ambiguous_table_name_map,
5018 )
5019
5020 @classmethod
5021 def _normalize_froms(
5022 cls,
5023 iterable_of_froms: Iterable[FromClause],
5024 check_statement: Optional[Select[Unpack[TupleAny]]] = None,
5025 ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None,
5026 ) -> List[FromClause]:
5027 """given an iterable of things to select FROM, reduce them to what
5028 would actually render in the FROM clause of a SELECT.
5029
5030 This does the job of checking for JOINs, tables, etc. that are in fact
5031 overlapping due to cloning, adaption, present in overlapping joins,
5032 etc.
5033
5034 """
5035 seen: Set[FromClause] = set()
5036 froms: List[FromClause] = []
5037
5038 for item in iterable_of_froms:
5039 if is_subquery(item) and item.element is check_statement:
5040 raise exc.InvalidRequestError(
5041 "select() construct refers to itself as a FROM"
5042 )
5043
5044 if not seen.intersection(item._cloned_set):
5045 froms.append(item)
5046 seen.update(item._cloned_set)
5047
5048 if froms:
5049 toremove = set(
5050 itertools.chain.from_iterable(
5051 [_expand_cloned(f._hide_froms) for f in froms]
5052 )
5053 )
5054 if toremove:
5055 # filter out to FROM clauses not in the list,
5056 # using a list to maintain ordering
5057 froms = [f for f in froms if f not in toremove]
5058
5059 if ambiguous_table_name_map is not None:
5060 ambiguous_table_name_map.update(
5061 (
5062 fr.name,
5063 _anonymous_label.safe_construct(
5064 hash(fr.name), fr.name
5065 ),
5066 )
5067 for item in froms
5068 for fr in item._from_objects
5069 if is_table(fr)
5070 and fr.schema
5071 and fr.name not in ambiguous_table_name_map
5072 )
5073
5074 return froms
5075
5076 def _get_display_froms(
5077 self,
5078 explicit_correlate_froms: Optional[Sequence[FromClause]] = None,
5079 implicit_correlate_froms: Optional[Sequence[FromClause]] = None,
5080 ) -> List[FromClause]:
5081 """Return the full list of 'from' clauses to be displayed.
5082
5083 Takes into account a set of existing froms which may be
5084 rendered in the FROM clause of enclosing selects; this Select
5085 may want to leave those absent if it is automatically
5086 correlating.
5087
5088 """
5089
5090 froms = self.froms
5091
5092 if self.statement._correlate:
5093 to_correlate = self.statement._correlate
5094 if to_correlate:
5095 to_remove = _cloned_intersection(
5096 _cloned_intersection(
5097 froms, explicit_correlate_froms or ()
5098 ),
5099 to_correlate,
5100 )
5101 froms = [f for f in froms if f not in to_remove]
5102
5103 if self.statement._correlate_except is not None:
5104 to_remove = _cloned_difference(
5105 _cloned_intersection(froms, explicit_correlate_froms or ()),
5106 self.statement._correlate_except,
5107 )
5108 froms = [f for f in froms if f not in to_remove]
5109
5110 if (
5111 self.statement._auto_correlate
5112 and implicit_correlate_froms
5113 and len(froms) > 1
5114 ):
5115 to_remove = _cloned_intersection(froms, implicit_correlate_froms)
5116 froms = [f for f in froms if f not in to_remove]
5117
5118 if not len(froms):
5119 raise exc.InvalidRequestError(
5120 "Select statement '%r"
5121 "' returned no FROM clauses "
5122 "due to auto-correlation; "
5123 "specify correlate(<tables>) "
5124 "to control correlation "
5125 "manually." % self.statement
5126 )
5127
5128 return froms
5129
5130 def _memoized_attr__label_resolve_dict(
5131 self,
5132 ) -> Tuple[
5133 Dict[str, ColumnElement[Any]],
5134 Dict[str, ColumnElement[Any]],
5135 Dict[str, ColumnElement[Any]],
5136 ]:
5137 with_cols: Dict[str, ColumnElement[Any]] = {
5138 c._tq_label or c.key: c
5139 for c in self.statement._all_selected_columns
5140 if c._allow_label_resolve
5141 }
5142 only_froms: Dict[str, ColumnElement[Any]] = {
5143 c.key: c # type: ignore[misc]
5144 for c in _select_iterables(self.froms)
5145 if c._allow_label_resolve
5146 }
5147 only_cols: Dict[str, ColumnElement[Any]] = with_cols.copy()
5148 for key, value in only_froms.items():
5149 with_cols.setdefault(key, value)
5150
5151 return with_cols, only_froms, only_cols
5152
5153 @classmethod
5154 def _get_filter_by_entities(
5155 cls, statement: Select[Unpack[TupleAny]]
5156 ) -> Collection[
5157 Union[FromClause, _JoinTargetProtocol, ColumnElement[Any]]
5158 ]:
5159 """Return all entities to search for filter_by() attributes.
5160
5161 This includes:
5162
5163 * All joined entities from _setup_joins
5164 * Memoized entities from previous operations (e.g.,
5165 before with_only_columns)
5166 * Explicit FROM objects from _from_obj
5167 * Entities inferred from _raw_columns
5168
5169 .. versionadded:: 2.1
5170
5171 """
5172 entities: set[
5173 Union[FromClause, _JoinTargetProtocol, ColumnElement[Any]]
5174 ]
5175
5176 entities = set(
5177 join_element[0] for join_element in statement._setup_joins
5178 )
5179
5180 for memoized in statement._memoized_select_entities:
5181 entities.update(
5182 join_element[0] for join_element in memoized._setup_joins
5183 )
5184
5185 entities.update(statement._from_obj)
5186
5187 for col in statement._raw_columns:
5188 entities.update(col._from_objects)
5189
5190 return entities
5191
5192 @classmethod
5193 def all_selected_columns(
5194 cls, statement: Select[Unpack[TupleAny]]
5195 ) -> _SelectIterable:
5196 return [c for c in _select_iterables(statement._raw_columns)]
5197
5198 def _setup_joins(
5199 self,
5200 args: Tuple[_SetupJoinsElement, ...],
5201 raw_columns: List[_ColumnsClauseElement],
5202 ) -> None:
5203 for right, onclause, left, flags in args:
5204 if TYPE_CHECKING:
5205 if onclause is not None:
5206 assert isinstance(onclause, ColumnElement)
5207
5208 explicit_left = left
5209 isouter = flags["isouter"]
5210 full = flags["full"]
5211
5212 if left is None:
5213 (
5214 left,
5215 replace_from_obj_index,
5216 ) = self._join_determine_implicit_left_side(
5217 raw_columns, left, right, onclause
5218 )
5219 else:
5220 replace_from_obj_index = self._join_place_explicit_left_side(
5221 left
5222 )
5223
5224 # these assertions can be made here, as if the right/onclause
5225 # contained ORM elements, the select() statement would have been
5226 # upgraded to an ORM select, and this method would not be called;
5227 # orm.context.ORMSelectCompileState._join() would be
5228 # used instead.
5229 if TYPE_CHECKING:
5230 assert isinstance(right, FromClause)
5231 if onclause is not None:
5232 assert isinstance(onclause, ColumnElement)
5233
5234 if replace_from_obj_index is not None:
5235 # splice into an existing element in the
5236 # self._from_obj list
5237 left_clause = self.from_clauses[replace_from_obj_index]
5238
5239 if explicit_left is not None and onclause is None:
5240 onclause = Join._join_condition(explicit_left, right)
5241
5242 self.from_clauses = (
5243 self.from_clauses[:replace_from_obj_index]
5244 + (
5245 Join(
5246 left_clause,
5247 right,
5248 onclause,
5249 isouter=isouter,
5250 full=full,
5251 ),
5252 )
5253 + self.from_clauses[replace_from_obj_index + 1 :]
5254 )
5255 else:
5256 assert left is not None
5257 self.from_clauses = self.from_clauses + (
5258 Join(left, right, onclause, isouter=isouter, full=full),
5259 )
5260
5261 @util.preload_module("sqlalchemy.sql.util")
5262 def _join_determine_implicit_left_side(
5263 self,
5264 raw_columns: List[_ColumnsClauseElement],
5265 left: Optional[FromClause],
5266 right: _JoinTargetElement,
5267 onclause: Optional[ColumnElement[Any]],
5268 ) -> Tuple[Optional[FromClause], Optional[int]]:
5269 """When join conditions don't express the left side explicitly,
5270 determine if an existing FROM or entity in this query
5271 can serve as the left hand side.
5272
5273 """
5274
5275 sql_util = util.preloaded.sql_util
5276
5277 replace_from_obj_index: Optional[int] = None
5278
5279 from_clauses = self.from_clauses
5280
5281 if from_clauses:
5282 indexes: List[int] = sql_util.find_left_clause_to_join_from(
5283 from_clauses, right, onclause
5284 )
5285
5286 if len(indexes) == 1:
5287 replace_from_obj_index = indexes[0]
5288 left = from_clauses[replace_from_obj_index]
5289 else:
5290 potential = {}
5291 statement = self.statement
5292
5293 for from_clause in itertools.chain(
5294 itertools.chain.from_iterable(
5295 [element._from_objects for element in raw_columns]
5296 ),
5297 itertools.chain.from_iterable(
5298 [
5299 element._from_objects
5300 for element in statement._where_criteria
5301 ]
5302 ),
5303 ):
5304 potential[from_clause] = ()
5305
5306 all_clauses = list(potential.keys())
5307 indexes = sql_util.find_left_clause_to_join_from(
5308 all_clauses, right, onclause
5309 )
5310
5311 if len(indexes) == 1:
5312 left = all_clauses[indexes[0]]
5313
5314 if len(indexes) > 1:
5315 raise exc.InvalidRequestError(
5316 "Can't determine which FROM clause to join "
5317 "from, there are multiple FROMS which can "
5318 "join to this entity. Please use the .select_from() "
5319 "method to establish an explicit left side, as well as "
5320 "providing an explicit ON clause if not present already to "
5321 "help resolve the ambiguity."
5322 )
5323 elif not indexes:
5324 raise exc.InvalidRequestError(
5325 "Don't know how to join to %r. "
5326 "Please use the .select_from() "
5327 "method to establish an explicit left side, as well as "
5328 "providing an explicit ON clause if not present already to "
5329 "help resolve the ambiguity." % (right,)
5330 )
5331 return left, replace_from_obj_index
5332
5333 @util.preload_module("sqlalchemy.sql.util")
5334 def _join_place_explicit_left_side(
5335 self, left: FromClause
5336 ) -> Optional[int]:
5337 replace_from_obj_index: Optional[int] = None
5338
5339 sql_util = util.preloaded.sql_util
5340
5341 from_clauses = list(self.statement._iterate_from_elements())
5342
5343 if from_clauses:
5344 indexes: List[int] = sql_util.find_left_clause_that_matches_given(
5345 self.from_clauses, left
5346 )
5347 else:
5348 indexes = []
5349
5350 if len(indexes) > 1:
5351 raise exc.InvalidRequestError(
5352 "Can't identify which entity in which to assign the "
5353 "left side of this join. Please use a more specific "
5354 "ON clause."
5355 )
5356
5357 # have an index, means the left side is already present in
5358 # an existing FROM in the self._from_obj tuple
5359 if indexes:
5360 replace_from_obj_index = indexes[0]
5361
5362 # no index, means we need to add a new element to the
5363 # self._from_obj tuple
5364
5365 return replace_from_obj_index
5366
5367
5368class _SelectFromElements:
5369 __slots__ = ()
5370
5371 _raw_columns: List[_ColumnsClauseElement]
5372 _where_criteria: Tuple[ColumnElement[Any], ...]
5373 _from_obj: Tuple[FromClause, ...]
5374
5375 def _iterate_from_elements(self) -> Iterator[FromClause]:
5376 # note this does not include elements
5377 # in _setup_joins
5378
5379 seen = set()
5380 for element in self._raw_columns:
5381 for fr in element._from_objects:
5382 if fr in seen:
5383 continue
5384 seen.add(fr)
5385 yield fr
5386 for element in self._where_criteria:
5387 for fr in element._from_objects:
5388 if fr in seen:
5389 continue
5390 seen.add(fr)
5391 yield fr
5392 for element in self._from_obj:
5393 if element in seen:
5394 continue
5395 seen.add(element)
5396 yield element
5397
5398
5399class _MemoizedSelectEntities(
5400 cache_key.HasCacheKey, traversals.HasCopyInternals, visitors.Traversible
5401):
5402 """represents partial state from a Select object, for the case
5403 where Select.columns() has redefined the set of columns/entities the
5404 statement will be SELECTing from. This object represents
5405 the entities from the SELECT before that transformation was applied,
5406 so that transformations that were made in terms of the SELECT at that
5407 time, such as join() as well as options(), can access the correct context.
5408
5409 In previous SQLAlchemy versions, this wasn't needed because these
5410 constructs calculated everything up front, like when you called join()
5411 or options(), it did everything to figure out how that would translate
5412 into specific SQL constructs that would be ready to send directly to the
5413 SQL compiler when needed. But as of
5414 1.4, all of that stuff is done in the compilation phase, during the
5415 "compile state" portion of the process, so that the work can all be
5416 cached. So it needs to be able to resolve joins/options2 based on what
5417 the list of entities was when those methods were called.
5418
5419
5420 """
5421
5422 __visit_name__ = "memoized_select_entities"
5423
5424 _traverse_internals: _TraverseInternalsType = [
5425 ("_raw_columns", InternalTraversal.dp_clauseelement_list),
5426 ("_setup_joins", InternalTraversal.dp_setup_join_tuple),
5427 ("_with_options", InternalTraversal.dp_executable_options),
5428 ]
5429
5430 _is_clone_of: Optional[ClauseElement]
5431 _raw_columns: List[_ColumnsClauseElement]
5432 _setup_joins: Tuple[_SetupJoinsElement, ...]
5433 _with_options: Tuple[ExecutableOption, ...]
5434
5435 _annotations = util.EMPTY_DICT
5436
5437 def _clone(self, **kw: Any) -> Self:
5438 c = self.__class__.__new__(self.__class__)
5439 c.__dict__ = {k: v for k, v in self.__dict__.items()}
5440
5441 c._is_clone_of = self.__dict__.get("_is_clone_of", self)
5442 return c
5443
5444 @classmethod
5445 def _generate_for_statement(
5446 cls, select_stmt: Select[Unpack[TupleAny]]
5447 ) -> None:
5448 if select_stmt._setup_joins or select_stmt._with_options:
5449 self = _MemoizedSelectEntities()
5450 self._raw_columns = select_stmt._raw_columns
5451 self._setup_joins = select_stmt._setup_joins
5452 self._with_options = select_stmt._with_options
5453
5454 select_stmt._memoized_select_entities += (self,)
5455 select_stmt._raw_columns = []
5456 select_stmt._setup_joins = select_stmt._with_options = ()
5457
5458
5459class Select(
5460 HasPrefixes,
5461 HasSuffixes,
5462 HasHints,
5463 HasCompileState,
5464 HasSyntaxExtensions[
5465 Literal["post_select", "pre_columns", "post_criteria", "post_body"]
5466 ],
5467 _SelectFromElements,
5468 GenerativeSelect,
5469 TypedReturnsRows[Unpack[_Ts]],
5470):
5471 """Represents a ``SELECT`` statement.
5472
5473 The :class:`_sql.Select` object is normally constructed using the
5474 :func:`_sql.select` function. See that function for details.
5475
5476 Available extension points:
5477
5478 * ``post_select``: applies additional logic after the ``SELECT`` keyword.
5479 * ``pre_columns``: applies additional logic between the ``DISTINCT``
5480 keyword (if any) and the list of columns.
5481 * ``post_criteria``: applies additional logic after the ``HAVING`` clause.
5482 * ``post_body``: applies additional logic after the ``FOR UPDATE`` clause.
5483
5484 .. seealso::
5485
5486 :func:`_sql.select`
5487
5488 :ref:`tutorial_selecting_data` - in the 2.0 tutorial
5489
5490 """
5491
5492 __visit_name__ = "select"
5493
5494 _setup_joins: Tuple[_SetupJoinsElement, ...] = ()
5495 _memoized_select_entities: Tuple[TODO_Any, ...] = ()
5496
5497 _raw_columns: List[_ColumnsClauseElement]
5498
5499 _distinct: bool = False
5500 _distinct_on: Tuple[ColumnElement[Any], ...] = ()
5501 _correlate: Tuple[FromClause, ...] = ()
5502 _correlate_except: Optional[Tuple[FromClause, ...]] = None
5503 _where_criteria: Tuple[ColumnElement[Any], ...] = ()
5504 _having_criteria: Tuple[ColumnElement[Any], ...] = ()
5505 _from_obj: Tuple[FromClause, ...] = ()
5506
5507 _position_map = util.immutabledict(
5508 {
5509 "post_select": "_post_select_clause",
5510 "pre_columns": "_pre_columns_clause",
5511 "post_criteria": "_post_criteria_clause",
5512 "post_body": "_post_body_clause",
5513 }
5514 )
5515
5516 _post_select_clause: Optional[ClauseElement] = None
5517 """extension point for a ClauseElement that will be compiled directly
5518 after the SELECT keyword.
5519
5520 .. versionadded:: 2.1
5521
5522 """
5523
5524 _pre_columns_clause: Optional[ClauseElement] = None
5525 """extension point for a ClauseElement that will be compiled directly
5526 before the "columns" clause; after DISTINCT (if present).
5527
5528 .. versionadded:: 2.1
5529
5530 """
5531
5532 _post_criteria_clause: Optional[ClauseElement] = None
5533 """extension point for a ClauseElement that will be compiled directly
5534 after "criteria", following the HAVING clause but before ORDER BY.
5535
5536 .. versionadded:: 2.1
5537
5538 """
5539
5540 _post_body_clause: Optional[ClauseElement] = None
5541 """extension point for a ClauseElement that will be compiled directly
5542 after the "body", following the ORDER BY, LIMIT, and FOR UPDATE sections
5543 of the SELECT.
5544
5545 .. versionadded:: 2.1
5546
5547 """
5548
5549 _auto_correlate = True
5550 _is_select_statement = True
5551 _compile_options: CacheableOptions = (
5552 SelectState.default_select_compile_options
5553 )
5554
5555 _traverse_internals: _TraverseInternalsType = (
5556 [
5557 ("_raw_columns", InternalTraversal.dp_clauseelement_list),
5558 (
5559 "_memoized_select_entities",
5560 InternalTraversal.dp_memoized_select_entities,
5561 ),
5562 ("_from_obj", InternalTraversal.dp_clauseelement_list),
5563 ("_where_criteria", InternalTraversal.dp_clauseelement_tuple),
5564 ("_having_criteria", InternalTraversal.dp_clauseelement_tuple),
5565 ("_order_by_clauses", InternalTraversal.dp_clauseelement_tuple),
5566 ("_group_by_clauses", InternalTraversal.dp_clauseelement_tuple),
5567 ("_setup_joins", InternalTraversal.dp_setup_join_tuple),
5568 ("_correlate", InternalTraversal.dp_clauseelement_tuple),
5569 ("_correlate_except", InternalTraversal.dp_clauseelement_tuple),
5570 ("_limit_clause", InternalTraversal.dp_clauseelement),
5571 ("_offset_clause", InternalTraversal.dp_clauseelement),
5572 ("_fetch_clause", InternalTraversal.dp_clauseelement),
5573 ("_fetch_clause_options", InternalTraversal.dp_plain_dict),
5574 ("_for_update_arg", InternalTraversal.dp_clauseelement),
5575 ("_distinct", InternalTraversal.dp_boolean),
5576 ("_distinct_on", InternalTraversal.dp_clauseelement_tuple),
5577 ("_label_style", InternalTraversal.dp_plain_obj),
5578 ("_post_select_clause", InternalTraversal.dp_clauseelement),
5579 ("_pre_columns_clause", InternalTraversal.dp_clauseelement),
5580 ("_post_criteria_clause", InternalTraversal.dp_clauseelement),
5581 ("_post_body_clause", InternalTraversal.dp_clauseelement),
5582 ]
5583 + HasCTE._has_ctes_traverse_internals
5584 + HasPrefixes._has_prefixes_traverse_internals
5585 + HasSuffixes._has_suffixes_traverse_internals
5586 + HasHints._has_hints_traverse_internals
5587 + SupportsCloneAnnotations._clone_annotations_traverse_internals
5588 + ExecutableStatement._executable_traverse_internals
5589 + DialectKWArgs._dialect_kwargs_traverse_internals
5590 )
5591
5592 _cache_key_traversal: _CacheKeyTraversalType = _traverse_internals + [
5593 ("_compile_options", InternalTraversal.dp_has_cache_key)
5594 ]
5595
5596 _compile_state_factory: Type[SelectState]
5597
5598 @classmethod
5599 def _create_raw_select(cls, **kw: Any) -> Select[Unpack[TupleAny]]:
5600 """Create a :class:`.Select` using raw ``__new__`` with no coercions.
5601
5602 Used internally to build up :class:`.Select` constructs with
5603 pre-established state.
5604
5605 """
5606
5607 stmt = Select.__new__(Select)
5608 stmt.__dict__.update(kw)
5609 return stmt
5610
5611 def __init__(
5612 self, *entities: _ColumnsClauseArgument[Any], **dialect_kw: Any
5613 ):
5614 r"""Construct a new :class:`_expression.Select`.
5615
5616 The public constructor for :class:`_expression.Select` is the
5617 :func:`_sql.select` function.
5618
5619 """
5620 self._raw_columns = [
5621 coercions.expect(
5622 roles.ColumnsClauseRole, ent, apply_propagate_attrs=self
5623 )
5624 for ent in entities
5625 ]
5626 GenerativeSelect.__init__(self)
5627
5628 def _apply_syntax_extension_to_self(
5629 self, extension: SyntaxExtension
5630 ) -> None:
5631 extension.apply_to_select(self)
5632
5633 def _scalar_type(self) -> TypeEngine[Any]:
5634 if not self._raw_columns:
5635 return NULLTYPE
5636 elem = self._raw_columns[0]
5637 cols = list(elem._select_iterable)
5638 return cols[0].type
5639
5640 def filter(self, *criteria: _ColumnExpressionArgument[bool]) -> Self:
5641 """A synonym for the :meth:`_sql.Select.where` method."""
5642
5643 return self.where(*criteria)
5644
5645 if TYPE_CHECKING:
5646
5647 @overload
5648 def scalar_subquery(
5649 self: Select[_MAYBE_ENTITY],
5650 ) -> ScalarSelect[Any]: ...
5651
5652 @overload
5653 def scalar_subquery(
5654 self: Select[_NOT_ENTITY],
5655 ) -> ScalarSelect[_NOT_ENTITY]: ...
5656
5657 @overload
5658 def scalar_subquery(self) -> ScalarSelect[Any]: ...
5659
5660 def scalar_subquery(self) -> ScalarSelect[Any]: ...
5661
5662 def filter_by(self, **kwargs: Any) -> Self:
5663 r"""Apply the given filtering criterion as a WHERE clause
5664 to this select, using keyword expressions.
5665
5666 E.g.::
5667
5668 stmt = select(User).filter_by(name="some name")
5669
5670 Multiple criteria may be specified as comma separated; the effect
5671 is that they will be joined together using the :func:`.and_`
5672 function::
5673
5674 stmt = select(User).filter_by(name="some name", id=5)
5675
5676 The keyword expressions are extracted by searching across **all
5677 entities present in the FROM clause** of the statement. If a
5678 keyword name is present in more than one entity,
5679 :class:`_exc.AmbiguousColumnError` is raised. In this case, use
5680 :meth:`_sql.Select.filter` or :meth:`_sql.Select.where` with
5681 explicit column references::
5682
5683 # both User and Address have an 'id' attribute
5684 stmt = select(User).join(Address).filter_by(id=5)
5685 # raises AmbiguousColumnError
5686
5687 # use filter() with explicit qualification instead
5688 stmt = select(User).join(Address).filter(Address.id == 5)
5689
5690 .. versionchanged:: 2.1
5691
5692 :meth:`_sql.Select.filter_by` now searches across all FROM clause
5693 entities rather than only searching the last joined entity or first
5694 FROM entity. This allows the method to locate attributes
5695 unambiguously across multiple joined tables. The new
5696 :class:`_exc.AmbiguousColumnError` is raised when an attribute name
5697 is present in more than one entity.
5698
5699 See :ref:`change_8601` for migration notes.
5700
5701 .. seealso::
5702
5703 :ref:`tutorial_selecting_data` - in the :ref:`unified_tutorial`
5704
5705 :meth:`_sql.Select.filter` - filter on SQL expressions.
5706
5707 :meth:`_sql.Select.where` - filter on SQL expressions.
5708
5709 """
5710 # Get all entities via plugin system
5711 all_entities = SelectState.get_plugin_class(
5712 self
5713 )._get_filter_by_entities(self)
5714
5715 clauses = [
5716 _entity_namespace_key_search_all(all_entities, key) == value
5717 for key, value in kwargs.items()
5718 ]
5719 return self.filter(*clauses)
5720
5721 @property
5722 def column_descriptions(self) -> Any:
5723 """Return a :term:`plugin-enabled` 'column descriptions' structure
5724 referring to the columns which are SELECTed by this statement.
5725
5726 This attribute is generally useful when using the ORM, as an
5727 extended structure which includes information about mapped
5728 entities is returned. The section :ref:`queryguide_inspection`
5729 contains more background.
5730
5731 For a Core-only statement, the structure returned by this accessor
5732 is derived from the same objects that are returned by the
5733 :attr:`.Select.selected_columns` accessor, formatted as a list of
5734 dictionaries which contain the keys ``name``, ``type`` and ``expr``,
5735 which indicate the column expressions to be selected::
5736
5737 >>> stmt = select(user_table)
5738 >>> stmt.column_descriptions
5739 [
5740 {
5741 'name': 'id',
5742 'type': Integer(),
5743 'expr': Column('id', Integer(), ...)},
5744 {
5745 'name': 'name',
5746 'type': String(length=30),
5747 'expr': Column('name', String(length=30), ...)}
5748 ]
5749
5750 .. versionchanged:: 1.4.33 The :attr:`.Select.column_descriptions`
5751 attribute returns a structure for a Core-only set of entities,
5752 not just ORM-only entities.
5753
5754 .. seealso::
5755
5756 :attr:`.UpdateBase.entity_description` - entity information for
5757 an :func:`.insert`, :func:`.update`, or :func:`.delete`
5758
5759 :ref:`queryguide_inspection` - ORM background
5760
5761 """
5762 meth = SelectState.get_plugin_class(self).get_column_descriptions
5763 return meth(self)
5764
5765 def from_statement(
5766 self, statement: roles.ReturnsRowsRole
5767 ) -> ExecutableReturnsRows:
5768 """Apply the columns which this :class:`.Select` would select
5769 onto another statement.
5770
5771 This operation is :term:`plugin-specific` and will raise a not
5772 supported exception if this :class:`_sql.Select` does not select from
5773 plugin-enabled entities.
5774
5775
5776 The statement is typically either a :func:`_expression.text` or
5777 :func:`_expression.select` construct, and should return the set of
5778 columns appropriate to the entities represented by this
5779 :class:`.Select`.
5780
5781 .. seealso::
5782
5783 :ref:`orm_queryguide_selecting_text` - usage examples in the
5784 ORM Querying Guide
5785
5786 """
5787 meth = SelectState.get_plugin_class(self).from_statement
5788 return meth(self, statement)
5789
5790 @_generative
5791 def join(
5792 self,
5793 target: _JoinTargetArgument,
5794 onclause: Optional[_OnClauseArgument] = None,
5795 *,
5796 isouter: bool = False,
5797 full: bool = False,
5798 ) -> Self:
5799 r"""Create a SQL JOIN against this :class:`_expression.Select`
5800 object's criterion
5801 and apply generatively, returning the newly resulting
5802 :class:`_expression.Select`.
5803
5804 E.g.::
5805
5806 stmt = select(user_table).join(
5807 address_table, user_table.c.id == address_table.c.user_id
5808 )
5809
5810 The above statement generates SQL similar to:
5811
5812 .. sourcecode:: sql
5813
5814 SELECT user.id, user.name
5815 FROM user
5816 JOIN address ON user.id = address.user_id
5817
5818 .. versionchanged:: 1.4 :meth:`_expression.Select.join` now creates
5819 a :class:`_sql.Join` object between a :class:`_sql.FromClause`
5820 source that is within the FROM clause of the existing SELECT,
5821 and a given target :class:`_sql.FromClause`, and then adds
5822 this :class:`_sql.Join` to the FROM clause of the newly generated
5823 SELECT statement. This is completely reworked from the behavior
5824 in 1.3, which would instead create a subquery of the entire
5825 :class:`_expression.Select` and then join that subquery to the
5826 target.
5827
5828 This is a **backwards incompatible change** as the previous behavior
5829 was mostly useless, producing an unnamed subquery rejected by
5830 most databases in any case. The new behavior is modeled after
5831 that of the very successful :meth:`_orm.Query.join` method in the
5832 ORM, in order to support the functionality of :class:`_orm.Query`
5833 being available by using a :class:`_sql.Select` object with an
5834 :class:`_orm.Session`.
5835
5836 See the notes for this change at :ref:`change_select_join`.
5837
5838
5839 :param target: target table to join towards
5840
5841 :param onclause: ON clause of the join. If omitted, an ON clause
5842 is generated automatically based on the :class:`_schema.ForeignKey`
5843 linkages between the two tables, if one can be unambiguously
5844 determined, otherwise an error is raised.
5845
5846 :param isouter: if True, generate LEFT OUTER join. Same as
5847 :meth:`_expression.Select.outerjoin`.
5848
5849 :param full: if True, generate FULL OUTER join.
5850
5851 .. seealso::
5852
5853 :ref:`tutorial_select_join` - in the :doc:`/tutorial/index`
5854
5855 :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel`
5856
5857 :meth:`_expression.Select.join_from`
5858
5859 :meth:`_expression.Select.outerjoin`
5860
5861 """ # noqa: E501
5862 join_target = coercions.expect(
5863 roles.JoinTargetRole, target, apply_propagate_attrs=self
5864 )
5865 if onclause is not None:
5866 onclause_element = coercions.expect(roles.OnClauseRole, onclause)
5867 else:
5868 onclause_element = None
5869
5870 self._setup_joins += (
5871 (
5872 join_target,
5873 onclause_element,
5874 None,
5875 {"isouter": isouter, "full": full},
5876 ),
5877 )
5878 return self
5879
5880 def outerjoin_from(
5881 self,
5882 from_: _FromClauseArgument,
5883 target: _JoinTargetArgument,
5884 onclause: Optional[_OnClauseArgument] = None,
5885 *,
5886 full: bool = False,
5887 ) -> Self:
5888 r"""Create a SQL LEFT OUTER JOIN against this
5889 :class:`_expression.Select` object's criterion and apply generatively,
5890 returning the newly resulting :class:`_expression.Select`.
5891
5892 Usage is the same as that of :meth:`_selectable.Select.join_from`.
5893
5894 """
5895 return self.join_from(
5896 from_, target, onclause=onclause, isouter=True, full=full
5897 )
5898
5899 @_generative
5900 def join_from(
5901 self,
5902 from_: _FromClauseArgument,
5903 target: _JoinTargetArgument,
5904 onclause: Optional[_OnClauseArgument] = None,
5905 *,
5906 isouter: bool = False,
5907 full: bool = False,
5908 ) -> Self:
5909 r"""Create a SQL JOIN against this :class:`_expression.Select`
5910 object's criterion
5911 and apply generatively, returning the newly resulting
5912 :class:`_expression.Select`.
5913
5914 E.g.::
5915
5916 stmt = select(user_table, address_table).join_from(
5917 user_table, address_table, user_table.c.id == address_table.c.user_id
5918 )
5919
5920 The above statement generates SQL similar to:
5921
5922 .. sourcecode:: sql
5923
5924 SELECT user.id, user.name, address.id, address.email, address.user_id
5925 FROM user JOIN address ON user.id = address.user_id
5926
5927 .. versionadded:: 1.4
5928
5929 :param from\_: the left side of the join, will be rendered in the
5930 FROM clause and is roughly equivalent to using the
5931 :meth:`.Select.select_from` method.
5932
5933 :param target: target table to join towards
5934
5935 :param onclause: ON clause of the join.
5936
5937 :param isouter: if True, generate LEFT OUTER join. Same as
5938 :meth:`_expression.Select.outerjoin`.
5939
5940 :param full: if True, generate FULL OUTER join.
5941
5942 .. seealso::
5943
5944 :ref:`tutorial_select_join` - in the :doc:`/tutorial/index`
5945
5946 :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel`
5947
5948 :meth:`_expression.Select.join`
5949
5950 """ # noqa: E501
5951
5952 # note the order of parsing from vs. target is important here, as we
5953 # are also deriving the source of the plugin (i.e. the subject mapper
5954 # in an ORM query) which should favor the "from_" over the "target"
5955
5956 from_ = coercions.expect(
5957 roles.FromClauseRole, from_, apply_propagate_attrs=self
5958 )
5959 join_target = coercions.expect(
5960 roles.JoinTargetRole, target, apply_propagate_attrs=self
5961 )
5962 if onclause is not None:
5963 onclause_element = coercions.expect(roles.OnClauseRole, onclause)
5964 else:
5965 onclause_element = None
5966
5967 self._setup_joins += (
5968 (
5969 join_target,
5970 onclause_element,
5971 from_,
5972 {"isouter": isouter, "full": full},
5973 ),
5974 )
5975 return self
5976
5977 def outerjoin(
5978 self,
5979 target: _JoinTargetArgument,
5980 onclause: Optional[_OnClauseArgument] = None,
5981 *,
5982 full: bool = False,
5983 ) -> Self:
5984 """Create a left outer join.
5985
5986 Parameters are the same as that of :meth:`_expression.Select.join`.
5987
5988 .. versionchanged:: 1.4 :meth:`_expression.Select.outerjoin` now
5989 creates a :class:`_sql.Join` object between a
5990 :class:`_sql.FromClause` source that is within the FROM clause of
5991 the existing SELECT, and a given target :class:`_sql.FromClause`,
5992 and then adds this :class:`_sql.Join` to the FROM clause of the
5993 newly generated SELECT statement. This is completely reworked
5994 from the behavior in 1.3, which would instead create a subquery of
5995 the entire
5996 :class:`_expression.Select` and then join that subquery to the
5997 target.
5998
5999 This is a **backwards incompatible change** as the previous behavior
6000 was mostly useless, producing an unnamed subquery rejected by
6001 most databases in any case. The new behavior is modeled after
6002 that of the very successful :meth:`_orm.Query.join` method in the
6003 ORM, in order to support the functionality of :class:`_orm.Query`
6004 being available by using a :class:`_sql.Select` object with an
6005 :class:`_orm.Session`.
6006
6007 See the notes for this change at :ref:`change_select_join`.
6008
6009 .. seealso::
6010
6011 :ref:`tutorial_select_join` - in the :doc:`/tutorial/index`
6012
6013 :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel`
6014
6015 :meth:`_expression.Select.join`
6016
6017 """
6018 return self.join(target, onclause=onclause, isouter=True, full=full)
6019
6020 def get_final_froms(self) -> Sequence[FromClause]:
6021 """Compute the final displayed list of :class:`_expression.FromClause`
6022 elements.
6023
6024 This method will run through the full computation required to
6025 determine what FROM elements will be displayed in the resulting
6026 SELECT statement, including shadowing individual tables with
6027 JOIN objects, as well as full computation for ORM use cases including
6028 eager loading clauses.
6029
6030 For ORM use, this accessor returns the **post compilation**
6031 list of FROM objects; this collection will include elements such as
6032 eagerly loaded tables and joins. The objects will **not** be
6033 ORM enabled and not work as a replacement for the
6034 :meth:`_sql.Select.select_froms` collection; additionally, the
6035 method is not well performing for an ORM enabled statement as it
6036 will incur the full ORM construction process.
6037
6038 To retrieve the FROM list that's implied by the "columns" collection
6039 passed to the :class:`_sql.Select` originally, use the
6040 :attr:`_sql.Select.columns_clause_froms` accessor.
6041
6042 To select from an alternative set of columns while maintaining the
6043 FROM list, use the :meth:`_sql.Select.with_only_columns` method and
6044 pass the
6045 :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
6046 parameter.
6047
6048 .. versionadded:: 1.4.23 - the :meth:`_sql.Select.get_final_froms`
6049 method replaces the previous :attr:`_sql.Select.froms` accessor,
6050 which is deprecated.
6051
6052 .. seealso::
6053
6054 :attr:`_sql.Select.columns_clause_froms`
6055
6056 """
6057 compiler = self._default_compiler()
6058
6059 return self._compile_state_factory(self, compiler)._get_display_froms()
6060
6061 @property
6062 @util.deprecated(
6063 "1.4.23",
6064 "The :attr:`_expression.Select.froms` attribute is moved to "
6065 "the :meth:`_expression.Select.get_final_froms` method.",
6066 )
6067 def froms(self) -> Sequence[FromClause]:
6068 """Return the displayed list of :class:`_expression.FromClause`
6069 elements.
6070
6071
6072 """
6073 return self.get_final_froms()
6074
6075 @property
6076 def columns_clause_froms(self) -> List[FromClause]:
6077 """Return the set of :class:`_expression.FromClause` objects implied
6078 by the columns clause of this SELECT statement.
6079
6080 .. versionadded:: 1.4.23
6081
6082 .. seealso::
6083
6084 :attr:`_sql.Select.froms` - "final" FROM list taking the full
6085 statement into account
6086
6087 :meth:`_sql.Select.with_only_columns` - makes use of this
6088 collection to set up a new FROM list
6089
6090 """
6091
6092 return SelectState.get_plugin_class(self).get_columns_clause_froms(
6093 self
6094 )
6095
6096 @property
6097 def inner_columns(self) -> _SelectIterable:
6098 """An iterator of all :class:`_expression.ColumnElement`
6099 expressions which would
6100 be rendered into the columns clause of the resulting SELECT statement.
6101
6102 This method is legacy as of 1.4 and is superseded by the
6103 :attr:`_expression.Select.exported_columns` collection.
6104
6105 """
6106
6107 return iter(self._all_selected_columns)
6108
6109 def is_derived_from(self, fromclause: Optional[FromClause]) -> bool:
6110 if fromclause is not None and self in fromclause._cloned_set:
6111 return True
6112
6113 for f in self._iterate_from_elements():
6114 if f.is_derived_from(fromclause):
6115 return True
6116 return False
6117
6118 def _copy_internals(
6119 self, clone: _CloneCallableType = _clone, **kw: Any
6120 ) -> None:
6121 # Select() object has been cloned and probably adapted by the
6122 # given clone function. Apply the cloning function to internal
6123 # objects
6124
6125 # 1. keep a dictionary of the froms we've cloned, and what
6126 # they've become. This allows us to ensure the same cloned from
6127 # is used when other items such as columns are "cloned"
6128
6129 all_the_froms = set(
6130 itertools.chain(
6131 _from_objects(*self._raw_columns),
6132 _from_objects(*self._where_criteria),
6133 _from_objects(*[elem[0] for elem in self._setup_joins]),
6134 )
6135 )
6136
6137 # do a clone for the froms we've gathered. what is important here
6138 # is if any of the things we are selecting from, like tables,
6139 # were converted into Join objects. if so, these need to be
6140 # added to _from_obj explicitly, because otherwise they won't be
6141 # part of the new state, as they don't associate themselves with
6142 # their columns.
6143 new_froms = {f: clone(f, **kw) for f in all_the_froms}
6144
6145 # 2. copy FROM collections, adding in joins that we've created.
6146 existing_from_obj = [clone(f, **kw) for f in self._from_obj]
6147 add_froms = (
6148 {f for f in new_froms.values() if isinstance(f, Join)}
6149 .difference(all_the_froms)
6150 .difference(existing_from_obj)
6151 )
6152
6153 self._from_obj = tuple(existing_from_obj) + tuple(add_froms)
6154
6155 # 3. clone everything else, making sure we use columns
6156 # corresponding to the froms we just made.
6157 def replace(
6158 obj: Union[BinaryExpression[Any], ColumnClause[Any]],
6159 **kw: Any,
6160 ) -> Optional[KeyedColumnElement[Any]]:
6161 if isinstance(obj, ColumnClause) and obj.table in new_froms:
6162 newelem = new_froms[obj.table].corresponding_column(obj)
6163 return newelem
6164 return None
6165
6166 kw["replace"] = replace
6167
6168 # copy everything else. for table-ish things like correlate,
6169 # correlate_except, setup_joins, these clone normally. For
6170 # column-expression oriented things like raw_columns, where_criteria,
6171 # order by, we get this from the new froms.
6172 super()._copy_internals(clone=clone, omit_attrs=("_from_obj",), **kw)
6173
6174 self._reset_memoizations()
6175
6176 def get_children(self, **kw: Any) -> Iterable[ClauseElement]:
6177 return itertools.chain(
6178 super().get_children(
6179 omit_attrs=("_from_obj", "_correlate", "_correlate_except"),
6180 **kw,
6181 ),
6182 self._iterate_from_elements(),
6183 )
6184
6185 @_generative
6186 def add_columns(
6187 self, *entities: _ColumnsClauseArgument[Any]
6188 ) -> Select[Unpack[TupleAny]]:
6189 r"""Return a new :func:`_expression.select` construct with
6190 the given entities appended to its columns clause.
6191
6192 E.g.::
6193
6194 my_select = my_select.add_columns(table.c.new_column)
6195
6196 The original expressions in the columns clause remain in place.
6197 To replace the original expressions with new ones, see the method
6198 :meth:`_expression.Select.with_only_columns`.
6199
6200 :param \*entities: column, table, or other entity expressions to be
6201 added to the columns clause
6202
6203 .. seealso::
6204
6205 :meth:`_expression.Select.with_only_columns` - replaces existing
6206 expressions rather than appending.
6207
6208 :ref:`orm_queryguide_select_multiple_entities` - ORM-centric
6209 example
6210
6211 """
6212 self._reset_memoizations()
6213
6214 self._raw_columns = self._raw_columns + [
6215 coercions.expect(
6216 roles.ColumnsClauseRole, column, apply_propagate_attrs=self
6217 )
6218 for column in entities
6219 ]
6220 return self
6221
6222 def _set_entities(
6223 self, entities: Iterable[_ColumnsClauseArgument[Any]]
6224 ) -> None:
6225 self._raw_columns = [
6226 coercions.expect(
6227 roles.ColumnsClauseRole, ent, apply_propagate_attrs=self
6228 )
6229 for ent in util.to_list(entities)
6230 ]
6231
6232 @util.deprecated(
6233 "1.4",
6234 "The :meth:`_expression.Select.column` method is deprecated and will "
6235 "be removed in a future release. Please use "
6236 ":meth:`_expression.Select.add_columns`",
6237 )
6238 def column(
6239 self, column: _ColumnsClauseArgument[Any]
6240 ) -> Select[Unpack[TupleAny]]:
6241 """Return a new :func:`_expression.select` construct with
6242 the given column expression added to its columns clause.
6243
6244 E.g.::
6245
6246 my_select = my_select.column(table.c.new_column)
6247
6248 See the documentation for
6249 :meth:`_expression.Select.with_only_columns`
6250 for guidelines on adding /replacing the columns of a
6251 :class:`_expression.Select` object.
6252
6253 """
6254 return self.add_columns(column)
6255
6256 @util.preload_module("sqlalchemy.sql.util")
6257 def reduce_columns(
6258 self, only_synonyms: bool = True
6259 ) -> Select[Unpack[TupleAny]]:
6260 """Return a new :func:`_expression.select` construct with redundantly
6261 named, equivalently-valued columns removed from the columns clause.
6262
6263 "Redundant" here means two columns where one refers to the
6264 other either based on foreign key, or via a simple equality
6265 comparison in the WHERE clause of the statement. The primary purpose
6266 of this method is to automatically construct a select statement
6267 with all uniquely-named columns, without the need to use
6268 table-qualified labels as
6269 :meth:`_expression.Select.set_label_style`
6270 does.
6271
6272 When columns are omitted based on foreign key, the referred-to
6273 column is the one that's kept. When columns are omitted based on
6274 WHERE equivalence, the first column in the columns clause is the
6275 one that's kept.
6276
6277 :param only_synonyms: when True, limit the removal of columns
6278 to those which have the same name as the equivalent. Otherwise,
6279 all columns that are equivalent to another are removed.
6280
6281 """
6282 woc: Select[Unpack[TupleAny]]
6283 woc = self.with_only_columns(
6284 *util.preloaded.sql_util.reduce_columns(
6285 self._all_selected_columns,
6286 only_synonyms=only_synonyms,
6287 *(self._where_criteria + self._from_obj),
6288 )
6289 )
6290 return woc
6291
6292 # START OVERLOADED FUNCTIONS self.with_only_columns Select 1-8 ", *, maintain_column_froms: bool =..." # noqa: E501
6293
6294 # code within this block is **programmatically,
6295 # statically generated** by tools/generate_tuple_map_overloads.py
6296
6297 @overload
6298 def with_only_columns(
6299 self, __ent0: _TCCA[_T0], /, *, maintain_column_froms: bool = ...
6300 ) -> Select[_T0]: ...
6301
6302 @overload
6303 def with_only_columns(
6304 self,
6305 __ent0: _TCCA[_T0],
6306 __ent1: _TCCA[_T1],
6307 /,
6308 *,
6309 maintain_column_froms: bool = ...,
6310 ) -> Select[_T0, _T1]: ...
6311
6312 @overload
6313 def with_only_columns(
6314 self,
6315 __ent0: _TCCA[_T0],
6316 __ent1: _TCCA[_T1],
6317 __ent2: _TCCA[_T2],
6318 /,
6319 *,
6320 maintain_column_froms: bool = ...,
6321 ) -> Select[_T0, _T1, _T2]: ...
6322
6323 @overload
6324 def with_only_columns(
6325 self,
6326 __ent0: _TCCA[_T0],
6327 __ent1: _TCCA[_T1],
6328 __ent2: _TCCA[_T2],
6329 __ent3: _TCCA[_T3],
6330 /,
6331 *,
6332 maintain_column_froms: bool = ...,
6333 ) -> Select[_T0, _T1, _T2, _T3]: ...
6334
6335 @overload
6336 def with_only_columns(
6337 self,
6338 __ent0: _TCCA[_T0],
6339 __ent1: _TCCA[_T1],
6340 __ent2: _TCCA[_T2],
6341 __ent3: _TCCA[_T3],
6342 __ent4: _TCCA[_T4],
6343 /,
6344 *,
6345 maintain_column_froms: bool = ...,
6346 ) -> Select[_T0, _T1, _T2, _T3, _T4]: ...
6347
6348 @overload
6349 def with_only_columns(
6350 self,
6351 __ent0: _TCCA[_T0],
6352 __ent1: _TCCA[_T1],
6353 __ent2: _TCCA[_T2],
6354 __ent3: _TCCA[_T3],
6355 __ent4: _TCCA[_T4],
6356 __ent5: _TCCA[_T5],
6357 /,
6358 *,
6359 maintain_column_froms: bool = ...,
6360 ) -> Select[_T0, _T1, _T2, _T3, _T4, _T5]: ...
6361
6362 @overload
6363 def with_only_columns(
6364 self,
6365 __ent0: _TCCA[_T0],
6366 __ent1: _TCCA[_T1],
6367 __ent2: _TCCA[_T2],
6368 __ent3: _TCCA[_T3],
6369 __ent4: _TCCA[_T4],
6370 __ent5: _TCCA[_T5],
6371 __ent6: _TCCA[_T6],
6372 /,
6373 *,
6374 maintain_column_froms: bool = ...,
6375 ) -> Select[_T0, _T1, _T2, _T3, _T4, _T5, _T6]: ...
6376
6377 @overload
6378 def with_only_columns(
6379 self,
6380 __ent0: _TCCA[_T0],
6381 __ent1: _TCCA[_T1],
6382 __ent2: _TCCA[_T2],
6383 __ent3: _TCCA[_T3],
6384 __ent4: _TCCA[_T4],
6385 __ent5: _TCCA[_T5],
6386 __ent6: _TCCA[_T6],
6387 __ent7: _TCCA[_T7],
6388 /,
6389 *entities: _ColumnsClauseArgument[Any],
6390 maintain_column_froms: bool = ...,
6391 ) -> Select[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, Unpack[TupleAny]]: ...
6392
6393 # END OVERLOADED FUNCTIONS self.with_only_columns
6394
6395 @overload
6396 def with_only_columns(
6397 self,
6398 *entities: _ColumnsClauseArgument[Any],
6399 maintain_column_froms: bool = False,
6400 **__kw: Any,
6401 ) -> Select[Unpack[TupleAny]]: ...
6402
6403 @_generative
6404 def with_only_columns(
6405 self,
6406 *entities: _ColumnsClauseArgument[Any],
6407 maintain_column_froms: bool = False,
6408 **__kw: Any,
6409 ) -> Select[Unpack[TupleAny]]:
6410 r"""Return a new :func:`_expression.select` construct with its columns
6411 clause replaced with the given entities.
6412
6413 By default, this method is exactly equivalent to as if the original
6414 :func:`_expression.select` had been called with the given entities.
6415 E.g. a statement::
6416
6417 s = select(table1.c.a, table1.c.b)
6418 s = s.with_only_columns(table1.c.b)
6419
6420 should be exactly equivalent to::
6421
6422 s = select(table1.c.b)
6423
6424 In this mode of operation, :meth:`_sql.Select.with_only_columns`
6425 will also dynamically alter the FROM clause of the
6426 statement if it is not explicitly stated.
6427 To maintain the existing set of FROMs including those implied by the
6428 current columns clause, add the
6429 :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
6430 parameter::
6431
6432 s = select(table1.c.a, table2.c.b)
6433 s = s.with_only_columns(table1.c.a, maintain_column_froms=True)
6434
6435 The above parameter performs a transfer of the effective FROMs
6436 in the columns collection to the :meth:`_sql.Select.select_from`
6437 method, as though the following were invoked::
6438
6439 s = select(table1.c.a, table2.c.b)
6440 s = s.select_from(table1, table2).with_only_columns(table1.c.a)
6441
6442 The :paramref:`_sql.Select.with_only_columns.maintain_column_froms`
6443 parameter makes use of the :attr:`_sql.Select.columns_clause_froms`
6444 collection and performs an operation equivalent to the following::
6445
6446 s = select(table1.c.a, table2.c.b)
6447 s = s.select_from(*s.columns_clause_froms).with_only_columns(table1.c.a)
6448
6449 :param \*entities: column expressions to be used.
6450
6451 :param maintain_column_froms: boolean parameter that will ensure the
6452 FROM list implied from the current columns clause will be transferred
6453 to the :meth:`_sql.Select.select_from` method first.
6454
6455 .. versionadded:: 1.4.23
6456
6457 """ # noqa: E501
6458
6459 if __kw:
6460 raise _no_kw()
6461
6462 # memoizations should be cleared here as of
6463 # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
6464 # is the case for now.
6465 self._assert_no_memoizations()
6466
6467 if maintain_column_froms:
6468 self.select_from.non_generative( # type: ignore[attr-defined]
6469 self, *self.columns_clause_froms
6470 )
6471
6472 # then memoize the FROMs etc.
6473 _MemoizedSelectEntities._generate_for_statement(self)
6474
6475 self._raw_columns = [
6476 coercions.expect(roles.ColumnsClauseRole, c)
6477 for c in coercions._expression_collection_was_a_list(
6478 "entities", "Select.with_only_columns", entities
6479 )
6480 ]
6481 return self
6482
6483 @property
6484 def whereclause(self) -> Optional[ColumnElement[Any]]:
6485 """Return the completed WHERE clause for this
6486 :class:`_expression.Select` statement.
6487
6488 This assembles the current collection of WHERE criteria
6489 into a single :class:`_expression.BooleanClauseList` construct.
6490
6491
6492 .. versionadded:: 1.4
6493
6494 """
6495
6496 return BooleanClauseList._construct_for_whereclause(
6497 self._where_criteria
6498 )
6499
6500 _whereclause = whereclause
6501
6502 @_generative
6503 def where(self, *whereclause: _ColumnExpressionArgument[bool]) -> Self:
6504 """Return a new :func:`_expression.select` construct with
6505 the given expression added to
6506 its WHERE clause, joined to the existing clause via AND, if any.
6507
6508 """
6509
6510 assert isinstance(self._where_criteria, tuple)
6511
6512 for criterion in whereclause:
6513 where_criteria: ColumnElement[Any] = coercions.expect(
6514 roles.WhereHavingRole, criterion, apply_propagate_attrs=self
6515 )
6516 self._where_criteria += (where_criteria,)
6517 return self
6518
6519 @_generative
6520 def having(self, *having: _ColumnExpressionArgument[bool]) -> Self:
6521 """Return a new :func:`_expression.select` construct with
6522 the given expression added to
6523 its HAVING clause, joined to the existing clause via AND, if any.
6524
6525 """
6526
6527 for criterion in having:
6528 having_criteria = coercions.expect(
6529 roles.WhereHavingRole, criterion, apply_propagate_attrs=self
6530 )
6531 self._having_criteria += (having_criteria,)
6532 return self
6533
6534 @_generative
6535 def distinct(self, *expr: _ColumnExpressionArgument[Any]) -> Self:
6536 r"""Return a new :func:`_expression.select` construct which
6537 will apply DISTINCT to the SELECT statement overall.
6538
6539 E.g.::
6540
6541 from sqlalchemy import select
6542
6543 stmt = select(users_table.c.id, users_table.c.name).distinct()
6544
6545 The above would produce an statement resembling:
6546
6547 .. sourcecode:: sql
6548
6549 SELECT DISTINCT user.id, user.name FROM user
6550
6551 The method also historically accepted an ``*expr`` parameter which
6552 produced the PostgreSQL dialect-specific ``DISTINCT ON`` expression.
6553 This is now replaced using the :func:`_postgresql.distinct_on`
6554 extension::
6555
6556 from sqlalchemy import select
6557 from sqlalchemy.dialects.postgresql import distinct_on
6558
6559 stmt = select(users_table).ext(distinct_on(users_table.c.name))
6560
6561 Using this parameter on other backends which don't support this
6562 syntax will raise an error.
6563
6564 :param \*expr: optional column expressions. When present,
6565 the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>)``
6566 construct. A deprecation warning and/or :class:`_exc.CompileError`
6567 will be raised on other backends.
6568
6569 .. deprecated:: 2.1 Passing expressions to
6570 :meth:`_sql.Select.distinct` is deprecated, use
6571 :func:`_postgresql.distinct_on` instead.
6572
6573 .. deprecated:: 1.4 Using \*expr in other dialects is deprecated
6574 and will raise :class:`_exc.CompileError` in a future version.
6575
6576 .. seealso::
6577
6578 :func:`_postgresql.distinct_on`
6579
6580 :meth:`.ext`
6581 """
6582 self._distinct = True
6583 if expr:
6584 warn_deprecated(
6585 "Passing expression to ``distinct`` to generate a "
6586 "DISTINCT ON clause is deprecated. Use instead the "
6587 "``postgresql.distinct_on`` function as an extension.",
6588 "2.1",
6589 )
6590 self._distinct_on = self._distinct_on + tuple(
6591 coercions.expect(roles.ByOfRole, e, apply_propagate_attrs=self)
6592 for e in expr
6593 )
6594 return self
6595
6596 @_generative
6597 def select_from(self, *froms: _FromClauseArgument) -> Self:
6598 r"""Return a new :func:`_expression.select` construct with the
6599 given FROM expression(s)
6600 merged into its list of FROM objects.
6601
6602 E.g.::
6603
6604 table1 = table("t1", column("a"))
6605 table2 = table("t2", column("b"))
6606 s = select(table1.c.a).select_from(
6607 table1.join(table2, table1.c.a == table2.c.b)
6608 )
6609
6610 The "from" list is a unique set on the identity of each element,
6611 so adding an already present :class:`_schema.Table`
6612 or other selectable
6613 will have no effect. Passing a :class:`_expression.Join` that refers
6614 to an already present :class:`_schema.Table`
6615 or other selectable will have
6616 the effect of concealing the presence of that selectable as
6617 an individual element in the rendered FROM list, instead
6618 rendering it into a JOIN clause.
6619
6620 While the typical purpose of :meth:`_expression.Select.select_from`
6621 is to
6622 replace the default, derived FROM clause with a join, it can
6623 also be called with individual table elements, multiple times
6624 if desired, in the case that the FROM clause cannot be fully
6625 derived from the columns clause::
6626
6627 select(func.count("*")).select_from(table1)
6628
6629 """
6630
6631 self._from_obj += tuple(
6632 coercions.expect(
6633 roles.FromClauseRole, fromclause, apply_propagate_attrs=self
6634 )
6635 for fromclause in froms
6636 )
6637 return self
6638
6639 @_generative
6640 def correlate(
6641 self,
6642 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
6643 ) -> Self:
6644 r"""Return a new :class:`_expression.Select`
6645 which will correlate the given FROM
6646 clauses to that of an enclosing :class:`_expression.Select`.
6647
6648 Calling this method turns off the :class:`_expression.Select` object's
6649 default behavior of "auto-correlation". Normally, FROM elements
6650 which appear in a :class:`_expression.Select`
6651 that encloses this one via
6652 its :term:`WHERE clause`, ORDER BY, HAVING or
6653 :term:`columns clause` will be omitted from this
6654 :class:`_expression.Select`
6655 object's :term:`FROM clause`.
6656 Setting an explicit correlation collection using the
6657 :meth:`_expression.Select.correlate`
6658 method provides a fixed list of FROM objects
6659 that can potentially take place in this process.
6660
6661 When :meth:`_expression.Select.correlate`
6662 is used to apply specific FROM clauses
6663 for correlation, the FROM elements become candidates for
6664 correlation regardless of how deeply nested this
6665 :class:`_expression.Select`
6666 object is, relative to an enclosing :class:`_expression.Select`
6667 which refers to
6668 the same FROM object. This is in contrast to the behavior of
6669 "auto-correlation" which only correlates to an immediate enclosing
6670 :class:`_expression.Select`.
6671 Multi-level correlation ensures that the link
6672 between enclosed and enclosing :class:`_expression.Select`
6673 is always via
6674 at least one WHERE/ORDER BY/HAVING/columns clause in order for
6675 correlation to take place.
6676
6677 If ``None`` is passed, the :class:`_expression.Select`
6678 object will correlate
6679 none of its FROM entries, and all will render unconditionally
6680 in the local FROM clause.
6681
6682 :param \*fromclauses: one or more :class:`.FromClause` or other
6683 FROM-compatible construct such as an ORM mapped entity to become part
6684 of the correlate collection; alternatively pass a single value
6685 ``None`` to remove all existing correlations.
6686
6687 .. seealso::
6688
6689 :meth:`_expression.Select.correlate_except`
6690
6691 :ref:`tutorial_scalar_subquery`
6692
6693 """
6694
6695 # tests failing when we try to change how these
6696 # arguments are passed
6697
6698 self._auto_correlate = False
6699 if not fromclauses or fromclauses[0] in {None, False}:
6700 if len(fromclauses) > 1:
6701 raise exc.ArgumentError(
6702 "additional FROM objects not accepted when "
6703 "passing None/False to correlate()"
6704 )
6705 self._correlate = ()
6706 else:
6707 self._correlate = self._correlate + tuple(
6708 coercions.expect(roles.FromClauseRole, f) for f in fromclauses
6709 )
6710 return self
6711
6712 @_generative
6713 def correlate_except(
6714 self,
6715 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
6716 ) -> Self:
6717 r"""Return a new :class:`_expression.Select`
6718 which will omit the given FROM
6719 clauses from the auto-correlation process.
6720
6721 Calling :meth:`_expression.Select.correlate_except` turns off the
6722 :class:`_expression.Select` object's default behavior of
6723 "auto-correlation" for the given FROM elements. An element
6724 specified here will unconditionally appear in the FROM list, while
6725 all other FROM elements remain subject to normal auto-correlation
6726 behaviors.
6727
6728 If ``None`` is passed, or no arguments are passed,
6729 the :class:`_expression.Select` object will correlate all of its
6730 FROM entries.
6731
6732 :param \*fromclauses: a list of one or more
6733 :class:`_expression.FromClause`
6734 constructs, or other compatible constructs (i.e. ORM-mapped
6735 classes) to become part of the correlate-exception collection.
6736
6737 .. seealso::
6738
6739 :meth:`_expression.Select.correlate`
6740
6741 :ref:`tutorial_scalar_subquery`
6742
6743 """
6744
6745 self._auto_correlate = False
6746 if not fromclauses or fromclauses[0] in {None, False}:
6747 if len(fromclauses) > 1:
6748 raise exc.ArgumentError(
6749 "additional FROM objects not accepted when "
6750 "passing None/False to correlate_except()"
6751 )
6752 self._correlate_except = ()
6753 else:
6754 self._correlate_except = (self._correlate_except or ()) + tuple(
6755 coercions.expect(roles.FromClauseRole, f) for f in fromclauses
6756 )
6757
6758 return self
6759
6760 @HasMemoized_ro_memoized_attribute
6761 def selected_columns(
6762 self,
6763 ) -> ColumnCollection[str, ColumnElement[Any]]:
6764 """A :class:`_expression.ColumnCollection`
6765 representing the columns that
6766 this SELECT statement or similar construct returns in its result set,
6767 not including :class:`_sql.TextClause` constructs.
6768
6769 This collection differs from the :attr:`_expression.FromClause.columns`
6770 collection of a :class:`_expression.FromClause` in that the columns
6771 within this collection cannot be directly nested inside another SELECT
6772 statement; a subquery must be applied first which provides for the
6773 necessary parenthesization required by SQL.
6774
6775 For a :func:`_expression.select` construct, the collection here is
6776 exactly what would be rendered inside the "SELECT" statement, and the
6777 :class:`_expression.ColumnElement` objects are directly present as they
6778 were given, e.g.::
6779
6780 col1 = column("q", Integer)
6781 col2 = column("p", Integer)
6782 stmt = select(col1, col2)
6783
6784 Above, ``stmt.selected_columns`` would be a collection that contains
6785 the ``col1`` and ``col2`` objects directly. For a statement that is
6786 against a :class:`_schema.Table` or other
6787 :class:`_expression.FromClause`, the collection will use the
6788 :class:`_expression.ColumnElement` objects that are in the
6789 :attr:`_expression.FromClause.c` collection of the from element.
6790
6791 A use case for the :attr:`_sql.Select.selected_columns` collection is
6792 to allow the existing columns to be referenced when adding additional
6793 criteria, e.g.::
6794
6795 def filter_on_id(my_select, id):
6796 return my_select.where(my_select.selected_columns["id"] == id)
6797
6798
6799 stmt = select(MyModel)
6800
6801 # adds "WHERE id=:param" to the statement
6802 stmt = filter_on_id(stmt, 42)
6803
6804 .. note::
6805
6806 The :attr:`_sql.Select.selected_columns` collection does not
6807 include expressions established in the columns clause using the
6808 :func:`_sql.text` construct; these are silently omitted from the
6809 collection. To use plain textual column expressions inside of a
6810 :class:`_sql.Select` construct, use the :func:`_sql.literal_column`
6811 construct.
6812
6813
6814 .. versionadded:: 1.4
6815
6816 """
6817
6818 # compare to SelectState._generate_columns_plus_names, which
6819 # generates the actual names used in the SELECT string. that
6820 # method is more complex because it also renders columns that are
6821 # fully ambiguous, e.g. same column more than once.
6822 conv = cast(
6823 "Callable[[Any], str]",
6824 SelectState._column_naming_convention(self._label_style),
6825 )
6826
6827 cc: WriteableColumnCollection[str, ColumnElement[Any]] = (
6828 WriteableColumnCollection(
6829 [
6830 (conv(c), c)
6831 for c in self._all_selected_columns
6832 if is_column_element(c)
6833 ]
6834 )
6835 )
6836 return cc.as_readonly()
6837
6838 @HasMemoized_ro_memoized_attribute
6839 def _all_selected_columns(self) -> _SelectIterable:
6840 meth = SelectState.get_plugin_class(self).all_selected_columns
6841 return list(meth(self))
6842
6843 def _ensure_disambiguated_names(self) -> Select[Unpack[TupleAny]]:
6844 if self._label_style is LABEL_STYLE_NONE:
6845 self = self.set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY)
6846 return self
6847
6848 def _generate_fromclause_column_proxies(
6849 self,
6850 subquery: FromClause,
6851 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
6852 primary_key: ColumnSet,
6853 foreign_keys: Set[KeyedColumnElement[Any]],
6854 *,
6855 proxy_compound_columns: Optional[
6856 Iterable[Sequence[ColumnElement[Any]]]
6857 ] = None,
6858 ) -> None:
6859 """Generate column proxies to place in the exported ``.c``
6860 collection of a subquery."""
6861
6862 if proxy_compound_columns:
6863 extra_col_iterator = proxy_compound_columns
6864 prox = [
6865 c._make_proxy(
6866 subquery,
6867 key=proxy_key,
6868 name=required_label_name,
6869 name_is_truncatable=True,
6870 compound_select_cols=extra_cols,
6871 primary_key=primary_key,
6872 foreign_keys=foreign_keys,
6873 )
6874 for (
6875 (
6876 required_label_name,
6877 proxy_key,
6878 fallback_label_name,
6879 c,
6880 repeated,
6881 ),
6882 extra_cols,
6883 ) in (
6884 zip(
6885 self._generate_columns_plus_names(False),
6886 extra_col_iterator,
6887 )
6888 )
6889 if is_column_element(c)
6890 ]
6891 else:
6892 prox = [
6893 c._make_proxy(
6894 subquery,
6895 key=proxy_key,
6896 name=required_label_name,
6897 name_is_truncatable=True,
6898 primary_key=primary_key,
6899 foreign_keys=foreign_keys,
6900 )
6901 for (
6902 required_label_name,
6903 proxy_key,
6904 fallback_label_name,
6905 c,
6906 repeated,
6907 ) in (self._generate_columns_plus_names(False))
6908 if is_column_element(c)
6909 ]
6910
6911 columns._populate_separate_keys(prox)
6912
6913 def _needs_parens_for_grouping(self) -> bool:
6914 return self._has_row_limiting_clause or bool(
6915 self._order_by_clause.clauses
6916 )
6917
6918 def self_group(
6919 self, against: Optional[OperatorType] = None
6920 ) -> Union[SelectStatementGrouping[Self], Self]:
6921 """Return a 'grouping' construct as per the
6922 :class:`_expression.ClauseElement` specification.
6923
6924 This produces an element that can be embedded in an expression. Note
6925 that this method is called automatically as needed when constructing
6926 expressions and should not require explicit use.
6927
6928 """
6929 if (
6930 isinstance(against, CompoundSelect)
6931 and not self._needs_parens_for_grouping()
6932 ):
6933 return self
6934 else:
6935 return SelectStatementGrouping(self)
6936
6937 def union(
6938 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
6939 ) -> CompoundSelect[Unpack[_Ts]]:
6940 r"""Return a SQL ``UNION`` of this select() construct against
6941 the given selectables provided as positional arguments.
6942
6943 :param \*other: one or more elements with which to create a
6944 UNION.
6945
6946 .. versionchanged:: 1.4.28
6947
6948 multiple elements are now accepted.
6949
6950 :param \**kwargs: keyword arguments are forwarded to the constructor
6951 for the newly created :class:`_sql.CompoundSelect` object.
6952
6953 """
6954 return CompoundSelect._create_union(self, *other)
6955
6956 def union_all(
6957 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
6958 ) -> CompoundSelect[Unpack[_Ts]]:
6959 r"""Return a SQL ``UNION ALL`` of this select() construct against
6960 the given selectables provided as positional arguments.
6961
6962 :param \*other: one or more elements with which to create a
6963 UNION.
6964
6965 .. versionchanged:: 1.4.28
6966
6967 multiple elements are now accepted.
6968
6969 :param \**kwargs: keyword arguments are forwarded to the constructor
6970 for the newly created :class:`_sql.CompoundSelect` object.
6971
6972 """
6973 return CompoundSelect._create_union_all(self, *other)
6974
6975 def except_(
6976 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
6977 ) -> CompoundSelect[Unpack[_Ts]]:
6978 r"""Return a SQL ``EXCEPT`` of this select() construct against
6979 the given selectable provided as positional arguments.
6980
6981 :param \*other: one or more elements with which to create a
6982 UNION.
6983
6984 .. versionchanged:: 1.4.28
6985
6986 multiple elements are now accepted.
6987
6988 """
6989 return CompoundSelect._create_except(self, *other)
6990
6991 def except_all(
6992 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
6993 ) -> CompoundSelect[Unpack[_Ts]]:
6994 r"""Return a SQL ``EXCEPT ALL`` of this select() construct against
6995 the given selectables provided as positional arguments.
6996
6997 :param \*other: one or more elements with which to create a
6998 UNION.
6999
7000 .. versionchanged:: 1.4.28
7001
7002 multiple elements are now accepted.
7003
7004 """
7005 return CompoundSelect._create_except_all(self, *other)
7006
7007 def intersect(
7008 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
7009 ) -> CompoundSelect[Unpack[_Ts]]:
7010 r"""Return a SQL ``INTERSECT`` of this select() construct against
7011 the given selectables provided as positional arguments.
7012
7013 :param \*other: one or more elements with which to create a
7014 UNION.
7015
7016 .. versionchanged:: 1.4.28
7017
7018 multiple elements are now accepted.
7019
7020 :param \**kwargs: keyword arguments are forwarded to the constructor
7021 for the newly created :class:`_sql.CompoundSelect` object.
7022
7023 """
7024 return CompoundSelect._create_intersect(self, *other)
7025
7026 def intersect_all(
7027 self, *other: _SelectStatementForCompoundArgument[Unpack[_Ts]]
7028 ) -> CompoundSelect[Unpack[_Ts]]:
7029 r"""Return a SQL ``INTERSECT ALL`` of this select() construct
7030 against the given selectables provided as positional arguments.
7031
7032 :param \*other: one or more elements with which to create a
7033 UNION.
7034
7035 .. versionchanged:: 1.4.28
7036
7037 multiple elements are now accepted.
7038
7039 :param \**kwargs: keyword arguments are forwarded to the constructor
7040 for the newly created :class:`_sql.CompoundSelect` object.
7041
7042 """
7043 return CompoundSelect._create_intersect_all(self, *other)
7044
7045
7046class ScalarSelect(
7047 roles.InElementRole, Generative, GroupedElement, ColumnElement[_T]
7048):
7049 """Represent a scalar subquery.
7050
7051
7052 A :class:`_sql.ScalarSelect` is created by invoking the
7053 :meth:`_sql.SelectBase.scalar_subquery` method. The object
7054 then participates in other SQL expressions as a SQL column expression
7055 within the :class:`_sql.ColumnElement` hierarchy.
7056
7057 .. seealso::
7058
7059 :meth:`_sql.SelectBase.scalar_subquery`
7060
7061 :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
7062
7063 """
7064
7065 _traverse_internals: _TraverseInternalsType = [
7066 ("element", InternalTraversal.dp_clauseelement),
7067 ("type", InternalTraversal.dp_type),
7068 ]
7069
7070 _from_objects: List[FromClause] = []
7071 _is_from_container = True
7072 if not TYPE_CHECKING:
7073 _is_implicitly_boolean = False
7074 inherit_cache = True
7075
7076 element: SelectBase
7077
7078 def __init__(self, element: SelectBase) -> None:
7079 self.element = element
7080 self.type = element._scalar_type()
7081 self._propagate_attrs = element._propagate_attrs
7082
7083 def __getattr__(self, attr: str) -> Any:
7084 return getattr(self.element, attr)
7085
7086 def __getstate__(self) -> Dict[str, Any]:
7087 return {"element": self.element, "type": self.type}
7088
7089 def __setstate__(self, state: Dict[str, Any]) -> None:
7090 self.element = state["element"]
7091 self.type = state["type"]
7092
7093 @property
7094 def columns(self) -> NoReturn:
7095 raise exc.InvalidRequestError(
7096 "Scalar Select expression has no "
7097 "columns; use this object directly "
7098 "within a column-level expression."
7099 )
7100
7101 c = columns
7102
7103 @_generative
7104 def where(self, crit: _ColumnExpressionArgument[bool]) -> Self:
7105 """Apply a WHERE clause to the SELECT statement referred to
7106 by this :class:`_expression.ScalarSelect`.
7107
7108 """
7109 self.element = cast("Select[Unpack[TupleAny]]", self.element).where(
7110 crit
7111 )
7112 return self
7113
7114 def self_group(self, against: Optional[OperatorType] = None) -> Self:
7115 return self
7116
7117 def _ungroup(self) -> Self:
7118 return self
7119
7120 @_generative
7121 def correlate(
7122 self,
7123 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
7124 ) -> Self:
7125 r"""Return a new :class:`_expression.ScalarSelect`
7126 which will correlate the given FROM
7127 clauses to that of an enclosing :class:`_expression.Select`.
7128
7129 This method is mirrored from the :meth:`_sql.Select.correlate` method
7130 of the underlying :class:`_sql.Select`. The method applies the
7131 :meth:_sql.Select.correlate` method, then returns a new
7132 :class:`_sql.ScalarSelect` against that statement.
7133
7134 .. versionadded:: 1.4 Previously, the
7135 :meth:`_sql.ScalarSelect.correlate`
7136 method was only available from :class:`_sql.Select`.
7137
7138 :param \*fromclauses: a list of one or more
7139 :class:`_expression.FromClause`
7140 constructs, or other compatible constructs (i.e. ORM-mapped
7141 classes) to become part of the correlate collection.
7142
7143 .. seealso::
7144
7145 :meth:`_expression.ScalarSelect.correlate_except`
7146
7147 :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
7148
7149
7150 """
7151 self.element = cast(
7152 "Select[Unpack[TupleAny]]", self.element
7153 ).correlate(*fromclauses)
7154 return self
7155
7156 @_generative
7157 def correlate_except(
7158 self,
7159 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
7160 ) -> Self:
7161 r"""Return a new :class:`_expression.ScalarSelect`
7162 which will omit the given FROM
7163 clauses from the auto-correlation process.
7164
7165 This method is mirrored from the
7166 :meth:`_sql.Select.correlate_except` method of the underlying
7167 :class:`_sql.Select`. The method applies the
7168 :meth:_sql.Select.correlate_except` method, then returns a new
7169 :class:`_sql.ScalarSelect` against that statement.
7170
7171 .. versionadded:: 1.4 Previously, the
7172 :meth:`_sql.ScalarSelect.correlate_except`
7173 method was only available from :class:`_sql.Select`.
7174
7175 :param \*fromclauses: a list of one or more
7176 :class:`_expression.FromClause`
7177 constructs, or other compatible constructs (i.e. ORM-mapped
7178 classes) to become part of the correlate-exception collection.
7179
7180 .. seealso::
7181
7182 :meth:`_expression.ScalarSelect.correlate`
7183
7184 :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial
7185
7186
7187 """
7188
7189 self.element = cast(
7190 "Select[Unpack[TupleAny]]", self.element
7191 ).correlate_except(*fromclauses)
7192 return self
7193
7194
7195class Exists(UnaryExpression[bool]):
7196 """Represent an ``EXISTS`` clause.
7197
7198 See :func:`_sql.exists` for a description of usage.
7199
7200 An ``EXISTS`` clause can also be constructed from a :func:`_sql.select`
7201 instance by calling :meth:`_sql.SelectBase.exists`.
7202
7203 """
7204
7205 inherit_cache = True
7206
7207 def __init__(
7208 self,
7209 __argument: Optional[
7210 Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]]
7211 ] = None,
7212 /,
7213 ):
7214 s: ScalarSelect[Any]
7215
7216 # TODO: this seems like we should be using coercions for this
7217 if __argument is None:
7218 s = Select(literal_column("*")).scalar_subquery()
7219 elif isinstance(__argument, SelectBase):
7220 s = __argument.scalar_subquery()
7221 s._propagate_attrs = __argument._propagate_attrs
7222 elif isinstance(__argument, ScalarSelect):
7223 s = __argument
7224 else:
7225 s = Select(__argument).scalar_subquery()
7226
7227 UnaryExpression.__init__(
7228 self,
7229 s,
7230 operator=operators.exists,
7231 type_=type_api.BOOLEANTYPE,
7232 )
7233
7234 @util.ro_non_memoized_property
7235 def _from_objects(self) -> List[FromClause]:
7236 return []
7237
7238 def _regroup(
7239 self,
7240 fn: Callable[[Select[Unpack[TupleAny]]], Select[Unpack[TupleAny]]],
7241 ) -> ScalarSelect[Any]:
7242
7243 assert isinstance(self.element, ScalarSelect)
7244 element = self.element.element
7245 if not isinstance(element, Select):
7246 raise exc.InvalidRequestError(
7247 "Can only apply this operation to a plain SELECT construct"
7248 )
7249 new_element = fn(element)
7250
7251 return_value = new_element.scalar_subquery()
7252 return return_value
7253
7254 def select(self) -> Select[bool]:
7255 r"""Return a SELECT of this :class:`_expression.Exists`.
7256
7257 e.g.::
7258
7259 stmt = exists(some_table.c.id).where(some_table.c.id == 5).select()
7260
7261 This will produce a statement resembling:
7262
7263 .. sourcecode:: sql
7264
7265 SELECT EXISTS (SELECT id FROM some_table WHERE some_table = :param) AS anon_1
7266
7267 .. seealso::
7268
7269 :func:`_expression.select` - general purpose
7270 method which allows for arbitrary column lists.
7271
7272 """ # noqa
7273
7274 return Select(self)
7275
7276 def correlate(
7277 self,
7278 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
7279 ) -> Self:
7280 """Apply correlation to the subquery noted by this
7281 :class:`_sql.Exists`.
7282
7283 .. seealso::
7284
7285 :meth:`_sql.ScalarSelect.correlate`
7286
7287 """
7288 e = self._clone()
7289 e.element = self._regroup(
7290 lambda element: element.correlate(*fromclauses)
7291 )
7292 return e
7293
7294 def correlate_except(
7295 self,
7296 *fromclauses: Union[Literal[None, False], _FromClauseArgument],
7297 ) -> Self:
7298 """Apply correlation to the subquery noted by this
7299 :class:`_sql.Exists`.
7300
7301 .. seealso::
7302
7303 :meth:`_sql.ScalarSelect.correlate_except`
7304
7305 """
7306 e = self._clone()
7307 e.element = self._regroup(
7308 lambda element: element.correlate_except(*fromclauses)
7309 )
7310 return e
7311
7312 def select_from(self, *froms: _FromClauseArgument) -> Self:
7313 """Return a new :class:`_expression.Exists` construct,
7314 applying the given
7315 expression to the :meth:`_expression.Select.select_from`
7316 method of the select
7317 statement contained.
7318
7319 .. note:: it is typically preferable to build a :class:`_sql.Select`
7320 statement first, including the desired WHERE clause, then use the
7321 :meth:`_sql.SelectBase.exists` method to produce an
7322 :class:`_sql.Exists` object at once.
7323
7324 """
7325 e = self._clone()
7326 e.element = self._regroup(lambda element: element.select_from(*froms))
7327 return e
7328
7329 def with_hint(
7330 self,
7331 selectable: _FromClauseArgument,
7332 text: str,
7333 dialect_name: str = "*",
7334 ) -> Self:
7335 r"""Return a new :class:`_expression.Exists` construct, applying
7336 the given arguments to the
7337 :meth:`_expression.Select.with_hint` method of the select
7338 statement contained.
7339
7340 The hint is therefore rendered against the SELECT that's enclosed
7341 by the EXISTS expression. This includes :class:`_expression.Exists`
7342 objects that were generated by ORM constructs such as
7343 :meth:`_orm.PropComparator.any` and
7344 :meth:`_orm.PropComparator.has`::
7345
7346 stmt = select(User).where(
7347 User.addresses.any().with_hint(
7348 Address.__table__, "WITH (NOLOCK)", "mssql"
7349 )
7350 )
7351
7352 The above statement renders as:
7353
7354 .. sourcecode:: sql
7355
7356 SELECT users.id, users.name
7357 FROM users
7358 WHERE EXISTS (SELECT 1
7359 FROM addresses WITH (NOLOCK)
7360 WHERE users.id = addresses.user_id)
7361
7362 .. versionadded:: 2.1
7363
7364 .. seealso::
7365
7366 :meth:`_expression.Select.with_hint`
7367
7368 :meth:`_expression.Exists.with_statement_hint`
7369
7370 """
7371 e = self._clone()
7372 e.element = self._regroup(
7373 lambda element: element.with_hint(selectable, text, dialect_name)
7374 )
7375 return e
7376
7377 def with_statement_hint(self, text: str, dialect_name: str = "*") -> Self:
7378 r"""Return a new :class:`_expression.Exists` construct, applying
7379 the given arguments to the
7380 :meth:`_expression.Select.with_statement_hint` method of the select
7381 statement contained.
7382
7383 The hint is therefore rendered at the trailing end of the SELECT
7384 that's enclosed by the EXISTS expression, rather than at the end of
7385 the enclosing statement. As with
7386 :meth:`_expression.Exists.with_hint`, this includes
7387 :class:`_expression.Exists` objects that were generated by ORM
7388 constructs such as :meth:`_orm.PropComparator.any` and
7389 :meth:`_orm.PropComparator.has`::
7390
7391 has_address = User.addresses.any().with_statement_hint(
7392 "WITH (NOLOCK)", "mssql"
7393 )
7394 stmt = select(User).where(has_address)
7395
7396 The above statement renders as:
7397
7398 .. sourcecode:: sql
7399
7400 SELECT users.id, users.name
7401 FROM users
7402 WHERE EXISTS (SELECT 1
7403 FROM addresses
7404 WHERE users.id = addresses.user_id WITH (NOLOCK))
7405
7406 .. versionadded:: 2.1
7407
7408 .. seealso::
7409
7410 :meth:`_expression.Select.with_statement_hint`
7411
7412 :meth:`_expression.Exists.with_hint`
7413
7414 """
7415 e = self._clone()
7416 e.element = self._regroup(
7417 lambda element: element.with_statement_hint(text, dialect_name)
7418 )
7419 return e
7420
7421 def where(self, *clause: _ColumnExpressionArgument[bool]) -> Self:
7422 """Return a new :func:`_expression.exists` construct with the
7423 given expression added to
7424 its WHERE clause, joined to the existing clause via AND, if any.
7425
7426
7427 .. note:: it is typically preferable to build a :class:`_sql.Select`
7428 statement first, including the desired WHERE clause, then use the
7429 :meth:`_sql.SelectBase.exists` method to produce an
7430 :class:`_sql.Exists` object at once.
7431
7432 """
7433 e = self._clone()
7434 e.element = self._regroup(lambda element: element.where(*clause))
7435 return e
7436
7437
7438class TextualSelect(SelectBase, ExecutableReturnsRows, Generative):
7439 """Wrap a :class:`_expression.TextClause` construct within a
7440 :class:`_expression.SelectBase`
7441 interface.
7442
7443 This allows the :class:`_expression.TextClause` object to gain a
7444 ``.c`` collection
7445 and other FROM-like capabilities such as
7446 :meth:`_expression.FromClause.alias`,
7447 :meth:`_expression.SelectBase.cte`, etc.
7448
7449 The :class:`_expression.TextualSelect` construct is produced via the
7450 :meth:`_expression.TextClause.columns`
7451 method - see that method for details.
7452
7453 .. versionchanged:: 1.4 the :class:`_expression.TextualSelect`
7454 class was renamed
7455 from ``TextAsFrom``, to more correctly suit its role as a
7456 SELECT-oriented object and not a FROM clause.
7457
7458 .. seealso::
7459
7460 :func:`_expression.text`
7461
7462 :meth:`_expression.TextClause.columns` - primary creation interface.
7463
7464 """
7465
7466 __visit_name__ = "textual_select"
7467
7468 _label_style = LABEL_STYLE_NONE
7469
7470 _traverse_internals: _TraverseInternalsType = (
7471 [
7472 ("element", InternalTraversal.dp_clauseelement),
7473 ("column_args", InternalTraversal.dp_clauseelement_list),
7474 ]
7475 + SupportsCloneAnnotations._clone_annotations_traverse_internals
7476 + HasCTE._has_ctes_traverse_internals
7477 + ExecutableStatement._executable_traverse_internals
7478 )
7479
7480 _is_textual = True
7481
7482 is_text = True
7483 is_select = True
7484
7485 def __init__(
7486 self,
7487 text: TextClause,
7488 columns: List[_ColumnExpressionArgument[Any]],
7489 positional: bool = False,
7490 ) -> None:
7491 self._init(
7492 text,
7493 # convert for ORM attributes->columns, etc
7494 [
7495 coercions.expect(roles.LabeledColumnExprRole, c)
7496 for c in columns
7497 ],
7498 positional,
7499 )
7500
7501 def _init(
7502 self,
7503 text: AbstractTextClause,
7504 columns: List[NamedColumn[Any]],
7505 positional: bool = False,
7506 ) -> None:
7507 self.element = text
7508 self.column_args = columns
7509 self.positional = positional
7510
7511 @HasMemoized_ro_memoized_attribute
7512 def selected_columns(
7513 self,
7514 ) -> ColumnCollection[str, KeyedColumnElement[Any]]:
7515 """A :class:`_expression.ColumnCollection`
7516 representing the columns that
7517 this SELECT statement or similar construct returns in its result set,
7518 not including :class:`_sql.TextClause` constructs.
7519
7520 This collection differs from the :attr:`_expression.FromClause.columns`
7521 collection of a :class:`_expression.FromClause` in that the columns
7522 within this collection cannot be directly nested inside another SELECT
7523 statement; a subquery must be applied first which provides for the
7524 necessary parenthesization required by SQL.
7525
7526 For a :class:`_expression.TextualSelect` construct, the collection
7527 contains the :class:`_expression.ColumnElement` objects that were
7528 passed to the constructor, typically via the
7529 :meth:`_expression.TextClause.columns` method.
7530
7531
7532 .. versionadded:: 1.4
7533
7534 """
7535 return WriteableColumnCollection(
7536 (c.key, c) for c in self.column_args
7537 ).as_readonly()
7538
7539 @util.ro_non_memoized_property
7540 def _all_selected_columns(self) -> _SelectIterable:
7541 return self.column_args
7542
7543 def set_label_style(self, style: SelectLabelStyle) -> TextualSelect:
7544 return self
7545
7546 def _ensure_disambiguated_names(self) -> TextualSelect:
7547 return self
7548
7549 @_generative
7550 def bindparams(
7551 self,
7552 *binds: BindParameter[Any],
7553 **bind_as_values: Any,
7554 ) -> Self:
7555 self.element = self.element.bindparams(*binds, **bind_as_values)
7556 return self
7557
7558 def _generate_fromclause_column_proxies(
7559 self,
7560 fromclause: FromClause,
7561 columns: WriteableColumnCollection[str, KeyedColumnElement[Any]],
7562 primary_key: ColumnSet,
7563 foreign_keys: Set[KeyedColumnElement[Any]],
7564 *,
7565 proxy_compound_columns: Optional[
7566 Iterable[Sequence[ColumnElement[Any]]]
7567 ] = None,
7568 ) -> None:
7569 if TYPE_CHECKING:
7570 assert isinstance(fromclause, Subquery)
7571
7572 if proxy_compound_columns:
7573 columns._populate_separate_keys(
7574 c._make_proxy(
7575 fromclause,
7576 compound_select_cols=extra_cols,
7577 primary_key=primary_key,
7578 foreign_keys=foreign_keys,
7579 )
7580 for c, extra_cols in zip(
7581 self.column_args, proxy_compound_columns
7582 )
7583 )
7584 else:
7585 columns._populate_separate_keys(
7586 c._make_proxy(
7587 fromclause,
7588 primary_key=primary_key,
7589 foreign_keys=foreign_keys,
7590 )
7591 for c in self.column_args
7592 )
7593
7594 def _scalar_type(self) -> Union[TypeEngine[Any], Any]:
7595 return self.column_args[0].type
7596
7597
7598TextAsFrom = TextualSelect
7599"""Backwards compatibility with the previous name"""
7600
7601
7602class AnnotatedFromClause(Annotated):
7603 def _copy_internals(
7604 self,
7605 _annotations_traversal: bool = False,
7606 ind_cols_on_fromclause: bool = False,
7607 **kw: Any,
7608 ) -> None:
7609 super()._copy_internals(**kw)
7610
7611 # passed from annotations._shallow_annotate(), _deep_annotate(), etc.
7612 # the traversals used by annotations for these cases are not currently
7613 # designed around expecting that inner elements inside of
7614 # AnnotatedFromClause's element are also deep copied, so skip for these
7615 # cases. in other cases such as plain visitors.cloned_traverse(), we
7616 # expect this to happen. see issue #12915
7617 if not _annotations_traversal:
7618 ee = self._Annotated__element # type: ignore[attr-defined]
7619 ee._copy_internals(**kw)
7620
7621 if ind_cols_on_fromclause:
7622 # passed from annotations._deep_annotate(). See that function
7623 # for notes
7624 ee = self._Annotated__element # type: ignore[attr-defined]
7625 self.c = ee.__class__.c.fget(self) # type: ignore[misc]
7626
7627 @util.ro_memoized_property
7628 def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
7629 """proxy the .c collection of the underlying FromClause.
7630
7631 Originally implemented in 2008 as a simple load of the .c collection
7632 when the annotated construct was created (see d3621ae961a), in modern
7633 SQLAlchemy versions this can be expensive for statements constructed
7634 with ORM aliases. So for #8796 SQLAlchemy 2.0 we instead proxy
7635 it, which works just as well.
7636
7637 Two different use cases seem to require the collection either copied
7638 from the underlying one, or unique to this AnnotatedFromClause.
7639
7640 See test_selectable->test_annotated_corresponding_column
7641
7642 """
7643 ee = self._Annotated__element # type: ignore[attr-defined]
7644 return ee.c # type: ignore[no-any-return]