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