diff options
author | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
---|---|---|
committer | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:17:55 -0400 |
commit | 12cf076118570eebbff08c6b3090e0d4798447a1 (patch) | |
tree | 3ba25e17e3c3a5e82316558ba3864b955919ff72 /venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py | |
parent | c45662ff3923b34614ddcc8feb9195541166dcc5 (diff) |
no venv
Diffstat (limited to 'venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py')
-rw-r--r-- | venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py | 6913 |
1 files changed, 0 insertions, 6913 deletions
diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py deleted file mode 100644 index 65978f6..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py +++ /dev/null @@ -1,6913 +0,0 @@ -# sql/selectable.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""The :class:`_expression.FromClause` class of SQL expression elements, -representing -SQL tables and derived rowsets. - -""" - -from __future__ import annotations - -import collections -from enum import Enum -import itertools -from typing import AbstractSet -from typing import Any as TODO_Any -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import List -from typing import NamedTuple -from typing import NoReturn -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import cache_key -from . import coercions -from . import operators -from . import roles -from . import traversals -from . import type_api -from . import visitors -from ._typing import _ColumnsClauseArgument -from ._typing import _no_kw -from ._typing import _TP -from ._typing import is_column_element -from ._typing import is_select_statement -from ._typing import is_subquery -from ._typing import is_table -from ._typing import is_text_clause -from .annotation import Annotated -from .annotation import SupportsCloneAnnotations -from .base import _clone -from .base import _cloned_difference -from .base import _cloned_intersection -from .base import _entity_namespace_key -from .base import _EntityNamespace -from .base import _expand_cloned -from .base import _from_objects -from .base import _generative -from .base import _never_select_column -from .base import _NoArg -from .base import _select_iterables -from .base import CacheableOptions -from .base import ColumnCollection -from .base import ColumnSet -from .base import CompileState -from .base import DedupeColumnCollection -from .base import Executable -from .base import Generative -from .base import HasCompileState -from .base import HasMemoized -from .base import Immutable -from .coercions import _document_text_coercion -from .elements import _anonymous_label -from .elements import BindParameter -from .elements import BooleanClauseList -from .elements import ClauseElement -from .elements import ClauseList -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import DQLDMLClauseElement -from .elements import GroupedElement -from .elements import literal_column -from .elements import TableValuedColumn -from .elements import UnaryExpression -from .operators import OperatorType -from .sqltypes import NULLTYPE -from .visitors import _TraverseInternalsType -from .visitors import InternalTraversal -from .visitors import prefix_anon_map -from .. import exc -from .. import util -from ..util import HasMemoized_ro_memoized_attribute -from ..util.typing import Literal -from ..util.typing import Protocol -from ..util.typing import Self - -and_ = BooleanClauseList.and_ - -_T = TypeVar("_T", bound=Any) - -if TYPE_CHECKING: - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnExpressionOrStrLabelArgument - from ._typing import _FromClauseArgument - from ._typing import _JoinTargetArgument - from ._typing import _LimitOffsetType - from ._typing import _MAYBE_ENTITY - from ._typing import _NOT_ENTITY - from ._typing import _OnClauseArgument - from ._typing import _SelectStatementForCompoundArgument - from ._typing import _T0 - from ._typing import _T1 - from ._typing import _T2 - from ._typing import _T3 - from ._typing import _T4 - from ._typing import _T5 - from ._typing import _T6 - from ._typing import _T7 - from ._typing import _TextCoercedExpressionArgument - from ._typing import _TypedColumnClauseArgument as _TCCA - from ._typing import _TypeEngineArgument - from .base import _AmbiguousTableNameMap - from .base import ExecutableOption - from .base import ReadOnlyColumnCollection - from .cache_key import _CacheKeyTraversalType - from .compiler import SQLCompiler - from .dml import Delete - from .dml import Update - from .elements import BinaryExpression - from .elements import KeyedColumnElement - from .elements import Label - from .elements import NamedColumn - from .elements import TextClause - from .functions import Function - from .schema import ForeignKey - from .schema import ForeignKeyConstraint - from .sqltypes import TableValueType - from .type_api import TypeEngine - from .visitors import _CloneCallableType - - -_ColumnsClauseElement = Union["FromClause", ColumnElement[Any], "TextClause"] -_LabelConventionCallable = Callable[ - [Union["ColumnElement[Any]", "TextClause"]], Optional[str] -] - - -class _JoinTargetProtocol(Protocol): - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: ... - - @util.ro_non_memoized_property - def entity_namespace(self) -> _EntityNamespace: ... - - -_JoinTargetElement = Union["FromClause", _JoinTargetProtocol] -_OnClauseElement = Union["ColumnElement[bool]", _JoinTargetProtocol] - -_ForUpdateOfArgument = Union[ - # single column, Table, ORM Entity - Union[ - "_ColumnExpressionArgument[Any]", - "_FromClauseArgument", - ], - # or sequence of single column elements - Sequence["_ColumnExpressionArgument[Any]"], -] - - -_SetupJoinsElement = Tuple[ - _JoinTargetElement, - Optional[_OnClauseElement], - Optional["FromClause"], - Dict[str, Any], -] - - -_SelectIterable = Iterable[Union["ColumnElement[Any]", "TextClause"]] - - -class _OffsetLimitParam(BindParameter[int]): - inherit_cache = True - - @property - def _limit_offset_value(self) -> Optional[int]: - return self.effective_value - - -class ReturnsRows(roles.ReturnsRowsRole, DQLDMLClauseElement): - """The base-most class for Core constructs that have some concept of - columns that can represent rows. - - While the SELECT statement and TABLE are the primary things we think - of in this category, DML like INSERT, UPDATE and DELETE can also specify - RETURNING which means they can be used in CTEs and other forms, and - PostgreSQL has functions that return rows also. - - .. versionadded:: 1.4 - - """ - - _is_returns_rows = True - - # sub-elements of returns_rows - _is_from_clause = False - _is_select_base = False - _is_select_statement = False - _is_lateral = False - - @property - def selectable(self) -> ReturnsRows: - return self - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - """A sequence of column expression objects that represents the - "selected" columns of this :class:`_expression.ReturnsRows`. - - This is typically equivalent to .exported_columns except it is - delivered in the form of a straight sequence and not keyed - :class:`_expression.ColumnCollection`. - - """ - raise NotImplementedError() - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - """Return ``True`` if this :class:`.ReturnsRows` is - 'derived' from the given :class:`.FromClause`. - - An example would be an Alias of a Table is derived from that Table. - - """ - raise NotImplementedError() - - def _generate_fromclause_column_proxies( - self, fromclause: FromClause - ) -> None: - """Populate columns into an :class:`.AliasedReturnsRows` object.""" - - raise NotImplementedError() - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - """reset internal collections for an incoming column being added.""" - raise NotImplementedError() - - @property - def exported_columns(self) -> ReadOnlyColumnCollection[Any, Any]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.ReturnsRows`. - - The "exported" columns represent the collection of - :class:`_expression.ColumnElement` - expressions that are rendered by this SQL - construct. There are primary varieties which are the - "FROM clause columns" of a FROM clause, such as a table, join, - or subquery, the "SELECTed columns", which are the columns in - the "columns clause" of a SELECT statement, and the RETURNING - columns in a DML statement.. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.FromClause.exported_columns` - - :attr:`_expression.SelectBase.exported_columns` - """ - - raise NotImplementedError() - - -class ExecutableReturnsRows(Executable, ReturnsRows): - """base for executable statements that return rows.""" - - -class TypedReturnsRows(ExecutableReturnsRows, Generic[_TP]): - """base for executable statements that return rows.""" - - -class Selectable(ReturnsRows): - """Mark a class as being selectable.""" - - __visit_name__ = "selectable" - - is_selectable = True - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - raise NotImplementedError() - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a LATERAL alias of this :class:`_expression.Selectable`. - - The return value is the :class:`_expression.Lateral` construct also - provided by the top-level :func:`_expression.lateral` function. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - return Lateral._construct(self, name=name) - - @util.deprecated( - "1.4", - message="The :meth:`.Selectable.replace_selectable` method is " - "deprecated, and will be removed in a future release. Similar " - "functionality is available via the sqlalchemy.sql.visitors module.", - ) - @util.preload_module("sqlalchemy.sql.util") - def replace_selectable(self, old: FromClause, alias: Alias) -> Self: - """Replace all occurrences of :class:`_expression.FromClause` - 'old' with the given :class:`_expression.Alias` - object, returning a copy of this :class:`_expression.FromClause`. - - """ - return util.preloaded.sql_util.ClauseAdapter(alias).traverse(self) - - def corresponding_column( - self, column: KeyedColumnElement[Any], require_embedded: bool = False - ) -> Optional[KeyedColumnElement[Any]]: - """Given a :class:`_expression.ColumnElement`, return the exported - :class:`_expression.ColumnElement` object from the - :attr:`_expression.Selectable.exported_columns` - collection of this :class:`_expression.Selectable` - which corresponds to that - original :class:`_expression.ColumnElement` via a common ancestor - column. - - :param column: the target :class:`_expression.ColumnElement` - to be matched. - - :param require_embedded: only return corresponding columns for - the given :class:`_expression.ColumnElement`, if the given - :class:`_expression.ColumnElement` - is actually present within a sub-element - of this :class:`_expression.Selectable`. - Normally the column will match if - it merely shares a common ancestor with one of the exported - columns of this :class:`_expression.Selectable`. - - .. seealso:: - - :attr:`_expression.Selectable.exported_columns` - the - :class:`_expression.ColumnCollection` - that is used for the operation. - - :meth:`_expression.ColumnCollection.corresponding_column` - - implementation - method. - - """ - - return self.exported_columns.corresponding_column( - column, require_embedded - ) - - -class HasPrefixes: - _prefixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = () - - _has_prefixes_traverse_internals: _TraverseInternalsType = [ - ("_prefixes", InternalTraversal.dp_prefix_sequence) - ] - - @_generative - @_document_text_coercion( - "prefixes", - ":meth:`_expression.HasPrefixes.prefix_with`", - ":paramref:`.HasPrefixes.prefix_with.*prefixes`", - ) - def prefix_with( - self, - *prefixes: _TextCoercedExpressionArgument[Any], - dialect: str = "*", - ) -> Self: - r"""Add one or more expressions following the statement keyword, i.e. - SELECT, INSERT, UPDATE, or DELETE. Generative. - - This is used to support backend-specific prefix keywords such as those - provided by MySQL. - - E.g.:: - - stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql") - - # MySQL 5.7 optimizer hints - stmt = select(table).prefix_with( - "/*+ BKA(t1) */", dialect="mysql") - - Multiple prefixes can be specified by multiple calls - to :meth:`_expression.HasPrefixes.prefix_with`. - - :param \*prefixes: textual or :class:`_expression.ClauseElement` - construct which - will be rendered following the INSERT, UPDATE, or DELETE - keyword. - :param dialect: optional string dialect name which will - limit rendering of this prefix to only that dialect. - - """ - self._prefixes = self._prefixes + tuple( - [ - (coercions.expect(roles.StatementOptionRole, p), dialect) - for p in prefixes - ] - ) - return self - - -class HasSuffixes: - _suffixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = () - - _has_suffixes_traverse_internals: _TraverseInternalsType = [ - ("_suffixes", InternalTraversal.dp_prefix_sequence) - ] - - @_generative - @_document_text_coercion( - "suffixes", - ":meth:`_expression.HasSuffixes.suffix_with`", - ":paramref:`.HasSuffixes.suffix_with.*suffixes`", - ) - def suffix_with( - self, - *suffixes: _TextCoercedExpressionArgument[Any], - dialect: str = "*", - ) -> Self: - r"""Add one or more expressions following the statement as a whole. - - This is used to support backend-specific suffix keywords on - certain constructs. - - E.g.:: - - stmt = select(col1, col2).cte().suffix_with( - "cycle empno set y_cycle to 1 default 0", dialect="oracle") - - Multiple suffixes can be specified by multiple calls - to :meth:`_expression.HasSuffixes.suffix_with`. - - :param \*suffixes: textual or :class:`_expression.ClauseElement` - construct which - will be rendered following the target clause. - :param dialect: Optional string dialect name which will - limit rendering of this suffix to only that dialect. - - """ - self._suffixes = self._suffixes + tuple( - [ - (coercions.expect(roles.StatementOptionRole, p), dialect) - for p in suffixes - ] - ) - return self - - -class HasHints: - _hints: util.immutabledict[Tuple[FromClause, str], str] = ( - util.immutabledict() - ) - _statement_hints: Tuple[Tuple[str, str], ...] = () - - _has_hints_traverse_internals: _TraverseInternalsType = [ - ("_statement_hints", InternalTraversal.dp_statement_hint_list), - ("_hints", InternalTraversal.dp_table_hint_list), - ] - - def with_statement_hint(self, text: str, dialect_name: str = "*") -> Self: - """Add a statement hint to this :class:`_expression.Select` or - other selectable object. - - This method is similar to :meth:`_expression.Select.with_hint` - except that - it does not require an individual table, and instead applies to the - statement as a whole. - - Hints here are specific to the backend database and may include - directives such as isolation levels, file directives, fetch directives, - etc. - - .. seealso:: - - :meth:`_expression.Select.with_hint` - - :meth:`_expression.Select.prefix_with` - generic SELECT prefixing - which also can suit some database-specific HINT syntaxes such as - MySQL optimizer hints - - """ - return self._with_hint(None, text, dialect_name) - - @_generative - def with_hint( - self, - selectable: _FromClauseArgument, - text: str, - dialect_name: str = "*", - ) -> Self: - r"""Add an indexing or other executional context hint for the given - selectable to this :class:`_expression.Select` or other selectable - object. - - The text of the hint is rendered in the appropriate - location for the database backend in use, relative - to the given :class:`_schema.Table` or :class:`_expression.Alias` - passed as the - ``selectable`` argument. The dialect implementation - typically uses Python string substitution syntax - with the token ``%(name)s`` to render the name of - the table or alias. E.g. when using Oracle, the - following:: - - select(mytable).\ - with_hint(mytable, "index(%(name)s ix_mytable)") - - Would render SQL as:: - - select /*+ index(mytable ix_mytable) */ ... from mytable - - The ``dialect_name`` option will limit the rendering of a particular - hint to a particular backend. Such as, to add hints for both Oracle - and Sybase simultaneously:: - - select(mytable).\ - with_hint(mytable, "index(%(name)s ix_mytable)", 'oracle').\ - with_hint(mytable, "WITH INDEX ix_mytable", 'mssql') - - .. seealso:: - - :meth:`_expression.Select.with_statement_hint` - - """ - - return self._with_hint(selectable, text, dialect_name) - - def _with_hint( - self, - selectable: Optional[_FromClauseArgument], - text: str, - dialect_name: str, - ) -> Self: - if selectable is None: - self._statement_hints += ((dialect_name, text),) - else: - self._hints = self._hints.union( - { - ( - coercions.expect(roles.FromClauseRole, selectable), - dialect_name, - ): text - } - ) - return self - - -class FromClause(roles.AnonymizedFromClauseRole, Selectable): - """Represent an element that can be used within the ``FROM`` - clause of a ``SELECT`` statement. - - The most common forms of :class:`_expression.FromClause` are the - :class:`_schema.Table` and the :func:`_expression.select` constructs. Key - features common to all :class:`_expression.FromClause` objects include: - - * a :attr:`.c` collection, which provides per-name access to a collection - of :class:`_expression.ColumnElement` objects. - * a :attr:`.primary_key` attribute, which is a collection of all those - :class:`_expression.ColumnElement` - objects that indicate the ``primary_key`` flag. - * Methods to generate various derivations of a "from" clause, including - :meth:`_expression.FromClause.alias`, - :meth:`_expression.FromClause.join`, - :meth:`_expression.FromClause.select`. - - - """ - - __visit_name__ = "fromclause" - named_with_column = False - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return () - - _is_clone_of: Optional[FromClause] - - _columns: ColumnCollection[Any, Any] - - schema: Optional[str] = None - """Define the 'schema' attribute for this :class:`_expression.FromClause`. - - This is typically ``None`` for most objects except that of - :class:`_schema.Table`, where it is taken as the value of the - :paramref:`_schema.Table.schema` argument. - - """ - - is_selectable = True - _is_from_clause = True - _is_join = False - - _use_schema_map = False - - def select(self) -> Select[Any]: - r"""Return a SELECT of this :class:`_expression.FromClause`. - - - e.g.:: - - stmt = some_table.select().where(some_table.c.id == 5) - - .. seealso:: - - :func:`_expression.select` - general purpose - method which allows for arbitrary column lists. - - """ - return Select(self) - - def join( - self, - right: _FromClauseArgument, - onclause: Optional[_ColumnExpressionArgument[bool]] = None, - isouter: bool = False, - full: bool = False, - ) -> Join: - """Return a :class:`_expression.Join` from this - :class:`_expression.FromClause` - to another :class:`FromClause`. - - E.g.:: - - from sqlalchemy import join - - j = user_table.join(address_table, - user_table.c.id == address_table.c.user_id) - stmt = select(user_table).select_from(j) - - would emit SQL along the lines of:: - - SELECT user.id, user.name FROM user - JOIN address ON user.id = address.user_id - - :param right: the right side of the join; this is any - :class:`_expression.FromClause` object such as a - :class:`_schema.Table` object, and - may also be a selectable-compatible object such as an ORM-mapped - class. - - :param onclause: a SQL expression representing the ON clause of the - join. If left at ``None``, :meth:`_expression.FromClause.join` - will attempt to - join the two tables based on a foreign key relationship. - - :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN. - - :param full: if True, render a FULL OUTER JOIN, instead of LEFT OUTER - JOIN. Implies :paramref:`.FromClause.join.isouter`. - - .. seealso:: - - :func:`_expression.join` - standalone function - - :class:`_expression.Join` - the type of object produced - - """ - - return Join(self, right, onclause, isouter, full) - - def outerjoin( - self, - right: _FromClauseArgument, - onclause: Optional[_ColumnExpressionArgument[bool]] = None, - full: bool = False, - ) -> Join: - """Return a :class:`_expression.Join` from this - :class:`_expression.FromClause` - to another :class:`FromClause`, with the "isouter" flag set to - True. - - E.g.:: - - from sqlalchemy import outerjoin - - j = user_table.outerjoin(address_table, - user_table.c.id == address_table.c.user_id) - - The above is equivalent to:: - - j = user_table.join( - address_table, - user_table.c.id == address_table.c.user_id, - isouter=True) - - :param right: the right side of the join; this is any - :class:`_expression.FromClause` object such as a - :class:`_schema.Table` object, and - may also be a selectable-compatible object such as an ORM-mapped - class. - - :param onclause: a SQL expression representing the ON clause of the - join. If left at ``None``, :meth:`_expression.FromClause.join` - will attempt to - join the two tables based on a foreign key relationship. - - :param full: if True, render a FULL OUTER JOIN, instead of - LEFT OUTER JOIN. - - .. seealso:: - - :meth:`_expression.FromClause.join` - - :class:`_expression.Join` - - """ - - return Join(self, right, onclause, True, full) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> NamedFromClause: - """Return an alias of this :class:`_expression.FromClause`. - - E.g.:: - - a2 = some_table.alias('a2') - - The above code creates an :class:`_expression.Alias` - object which can be used - as a FROM clause in any SELECT statement. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - - return Alias._construct(self, name=name) - - def tablesample( - self, - sampling: Union[float, Function[Any]], - name: Optional[str] = None, - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> TableSample: - """Return a TABLESAMPLE alias of this :class:`_expression.FromClause`. - - The return value is the :class:`_expression.TableSample` - construct also - provided by the top-level :func:`_expression.tablesample` function. - - .. seealso:: - - :func:`_expression.tablesample` - usage guidelines and parameters - - """ - return TableSample._construct( - self, sampling=sampling, name=name, seed=seed - ) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - """Return ``True`` if this :class:`_expression.FromClause` is - 'derived' from the given ``FromClause``. - - An example would be an Alias of a Table is derived from that Table. - - """ - # this is essentially an "identity" check in the base class. - # Other constructs override this to traverse through - # contained elements. - return fromclause in self._cloned_set - - def _is_lexical_equivalent(self, other: FromClause) -> bool: - """Return ``True`` if this :class:`_expression.FromClause` and - the other represent the same lexical identity. - - This tests if either one is a copy of the other, or - if they are the same via annotation identity. - - """ - return bool(self._cloned_set.intersection(other._cloned_set)) - - @util.ro_non_memoized_property - def description(self) -> str: - """A brief description of this :class:`_expression.FromClause`. - - Used primarily for error message formatting. - - """ - return getattr(self, "name", self.__class__.__name__ + " object") - - def _generate_fromclause_column_proxies( - self, fromclause: FromClause - ) -> None: - fromclause._columns._populate_separate_keys( - col._make_proxy(fromclause) for col in self.c - ) - - @util.ro_non_memoized_property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.Selectable`. - - The "exported" columns for a :class:`_expression.FromClause` - object are synonymous - with the :attr:`_expression.FromClause.columns` collection. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.Selectable.exported_columns` - - :attr:`_expression.SelectBase.exported_columns` - - - """ - return self.c - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """A named-based collection of :class:`_expression.ColumnElement` - objects maintained by this :class:`_expression.FromClause`. - - The :attr:`.columns`, or :attr:`.c` collection, is the gateway - to the construction of SQL expressions using table-bound or - other selectable-bound columns:: - - select(mytable).where(mytable.c.somecolumn == 5) - - :return: a :class:`.ColumnCollection` object. - - """ - return self.c - - @util.ro_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """ - A synonym for :attr:`.FromClause.columns` - - :return: a :class:`.ColumnCollection` - - """ - if "_columns" not in self.__dict__: - self._init_collections() - self._populate_column_collection() - return self._columns.as_readonly() - - @util.ro_non_memoized_property - def entity_namespace(self) -> _EntityNamespace: - """Return a namespace used for name-based access in SQL expressions. - - This is the namespace that is used to resolve "filter_by()" type - expressions, such as:: - - stmt.filter_by(address='some address') - - It defaults to the ``.c`` collection, however internally it can - be overridden using the "entity_namespace" annotation to deliver - alternative results. - - """ - return self.c - - @util.ro_memoized_property - def primary_key(self) -> Iterable[NamedColumn[Any]]: - """Return the iterable collection of :class:`_schema.Column` objects - which comprise the primary key of this :class:`_selectable.FromClause`. - - For a :class:`_schema.Table` object, this collection is represented - by the :class:`_schema.PrimaryKeyConstraint` which itself is an - iterable collection of :class:`_schema.Column` objects. - - """ - self._init_collections() - self._populate_column_collection() - return self.primary_key - - @util.ro_memoized_property - def foreign_keys(self) -> Iterable[ForeignKey]: - """Return the collection of :class:`_schema.ForeignKey` marker objects - which this FromClause references. - - Each :class:`_schema.ForeignKey` is a member of a - :class:`_schema.Table`-wide - :class:`_schema.ForeignKeyConstraint`. - - .. seealso:: - - :attr:`_schema.Table.foreign_key_constraints` - - """ - self._init_collections() - self._populate_column_collection() - return self.foreign_keys - - def _reset_column_collection(self) -> None: - """Reset the attributes linked to the ``FromClause.c`` attribute. - - This collection is separate from all the other memoized things - as it has shown to be sensitive to being cleared out in situations - where enclosing code, typically in a replacement traversal scenario, - has already established strong relationships - with the exported columns. - - The collection is cleared for the case where a table is having a - column added to it as well as within a Join during copy internals. - - """ - - for key in ["_columns", "columns", "c", "primary_key", "foreign_keys"]: - self.__dict__.pop(key, None) - - @util.ro_non_memoized_property - def _select_iterable(self) -> _SelectIterable: - return (c for c in self.c if not _never_select_column(c)) - - def _init_collections(self) -> None: - assert "_columns" not in self.__dict__ - assert "primary_key" not in self.__dict__ - assert "foreign_keys" not in self.__dict__ - - self._columns = ColumnCollection() - self.primary_key = ColumnSet() # type: ignore - self.foreign_keys = set() # type: ignore - - @property - def _cols_populated(self) -> bool: - return "_columns" in self.__dict__ - - def _populate_column_collection(self) -> None: - """Called on subclasses to establish the .c collection. - - Each implementation has a different way of establishing - this collection. - - """ - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - """Given a column added to the .c collection of an underlying - selectable, produce the local version of that column, assuming this - selectable ultimately should proxy this column. - - this is used to "ping" a derived selectable to add a new column - to its .c. collection when a Column has been added to one of the - Table objects it ultimately derives from. - - If the given selectable hasn't populated its .c. collection yet, - it should at least pass on the message to the contained selectables, - but it will return None. - - This method is currently used by Declarative to allow Table - columns to be added to a partially constructed inheritance - mapping that may have already produced joins. The method - isn't public right now, as the full span of implications - and/or caveats aren't yet clear. - - It's also possible that this functionality could be invoked by - default via an event, which would require that - selectables maintain a weak referencing collection of all - derivations. - - """ - self._reset_column_collection() - - def _anonymous_fromclause( - self, *, name: Optional[str] = None, flat: bool = False - ) -> FromClause: - return self.alias(name=name) - - if TYPE_CHECKING: - - def self_group( - self, against: Optional[OperatorType] = None - ) -> Union[FromGrouping, Self]: ... - - -class NamedFromClause(FromClause): - """A :class:`.FromClause` that has a name. - - Examples include tables, subqueries, CTEs, aliased tables. - - .. versionadded:: 2.0 - - """ - - named_with_column = True - - name: str - - @util.preload_module("sqlalchemy.sql.sqltypes") - def table_valued(self) -> TableValuedColumn[Any]: - """Return a :class:`_sql.TableValuedColumn` object for this - :class:`_expression.FromClause`. - - A :class:`_sql.TableValuedColumn` is a :class:`_sql.ColumnElement` that - represents a complete row in a table. Support for this construct is - backend dependent, and is supported in various forms by backends - such as PostgreSQL, Oracle and SQL Server. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import select, column, func, table - >>> a = table("a", column("id"), column("x"), column("y")) - >>> stmt = select(func.row_to_json(a.table_valued())) - >>> print(stmt) - {printsql}SELECT row_to_json(a) AS row_to_json_1 - FROM a - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` - - """ - return TableValuedColumn(self, type_api.TABLEVALUE) - - -class SelectLabelStyle(Enum): - """Label style constants that may be passed to - :meth:`_sql.Select.set_label_style`.""" - - LABEL_STYLE_NONE = 0 - """Label style indicating no automatic labeling should be applied to the - columns clause of a SELECT statement. - - Below, the columns named ``columna`` are both rendered as is, meaning that - the name ``columna`` can only refer to the first occurrence of this name - within a result set, as well as if the statement were used as a subquery: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_NONE - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_NONE)) - {printsql}SELECT table1.columna, table1.columnb, table2.columna, table2.columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.Select.set_label_style` method. - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_TABLENAME_PLUS_COL = 1 - """Label style indicating all columns should be labeled as - ``<tablename>_<columnname>`` when generating the columns clause of a SELECT - statement, to disambiguate same-named columns referenced from different - tables, aliases, or subqueries. - - Below, all column names are given a label so that the two same-named - columns ``columna`` are disambiguated as ``table1_columna`` and - ``table2_columna``: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_TABLENAME_PLUS_COL - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)) - {printsql}SELECT table1.columna AS table1_columna, table1.columnb AS table1_columnb, table2.columna AS table2_columna, table2.columnc AS table2_columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.GenerativeSelect.set_label_style` method. - Equivalent to the legacy method ``Select.apply_labels()``; - :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` is SQLAlchemy's legacy - auto-labeling style. :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` provides a - less intrusive approach to disambiguation of same-named column expressions. - - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_DISAMBIGUATE_ONLY = 2 - """Label style indicating that columns with a name that conflicts with - an existing name should be labeled with a semi-anonymizing label - when generating the columns clause of a SELECT statement. - - Below, most column names are left unaffected, except for the second - occurrence of the name ``columna``, which is labeled using the - label ``columna_1`` to disambiguate it from that of ``tablea.columna``: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_DISAMBIGUATE_ONLY - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY)) - {printsql}SELECT table1.columna, table1.columnb, table2.columna AS columna_1, table2.columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.GenerativeSelect.set_label_style` method, - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` is the default labeling style - for all SELECT statements outside of :term:`1.x style` ORM queries. - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY - """The default label style, refers to - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`. - - .. versionadded:: 1.4 - - """ - - LABEL_STYLE_LEGACY_ORM = 3 - - -( - LABEL_STYLE_NONE, - LABEL_STYLE_TABLENAME_PLUS_COL, - LABEL_STYLE_DISAMBIGUATE_ONLY, - _, -) = list(SelectLabelStyle) - -LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY - - -class Join(roles.DMLTableRole, FromClause): - """Represent a ``JOIN`` construct between two - :class:`_expression.FromClause` - elements. - - The public constructor function for :class:`_expression.Join` - is the module-level - :func:`_expression.join()` function, as well as the - :meth:`_expression.FromClause.join` method - of any :class:`_expression.FromClause` (e.g. such as - :class:`_schema.Table`). - - .. seealso:: - - :func:`_expression.join` - - :meth:`_expression.FromClause.join` - - """ - - __visit_name__ = "join" - - _traverse_internals: _TraverseInternalsType = [ - ("left", InternalTraversal.dp_clauseelement), - ("right", InternalTraversal.dp_clauseelement), - ("onclause", InternalTraversal.dp_clauseelement), - ("isouter", InternalTraversal.dp_boolean), - ("full", InternalTraversal.dp_boolean), - ] - - _is_join = True - - left: FromClause - right: FromClause - onclause: Optional[ColumnElement[bool]] - isouter: bool - full: bool - - def __init__( - self, - left: _FromClauseArgument, - right: _FromClauseArgument, - onclause: Optional[_OnClauseArgument] = None, - isouter: bool = False, - full: bool = False, - ): - """Construct a new :class:`_expression.Join`. - - The usual entrypoint here is the :func:`_expression.join` - function or the :meth:`_expression.FromClause.join` method of any - :class:`_expression.FromClause` object. - - """ - - # when deannotate was removed here, callcounts went up for ORM - # compilation of eager joins, since there were more comparisons of - # annotated objects. test_orm.py -> test_fetch_results - # was therefore changed to show a more real-world use case, where the - # compilation is cached; there's no change in post-cache callcounts. - # callcounts for a single compilation in that particular test - # that includes about eight joins about 1100 extra fn calls, from - # 29200 -> 30373 - - self.left = coercions.expect( - roles.FromClauseRole, - left, - ) - self.right = coercions.expect( - roles.FromClauseRole, - right, - ).self_group() - - if onclause is None: - self.onclause = self._match_primaries(self.left, self.right) - else: - # note: taken from If91f61527236fd4d7ae3cad1f24c38be921c90ba - # not merged yet - self.onclause = coercions.expect( - roles.OnClauseRole, onclause - ).self_group(against=operators._asbool) - - self.isouter = isouter - self.full = full - - @util.ro_non_memoized_property - def description(self) -> str: - return "Join object on %s(%d) and %s(%d)" % ( - self.left.description, - id(self.left), - self.right.description, - id(self.right), - ) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - return ( - # use hash() to ensure direct comparison to annotated works - # as well - hash(fromclause) == hash(self) - or self.left.is_derived_from(fromclause) - or self.right.is_derived_from(fromclause) - ) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> FromGrouping: - ... - return FromGrouping(self) - - @util.preload_module("sqlalchemy.sql.util") - def _populate_column_collection(self) -> None: - sqlutil = util.preloaded.sql_util - columns: List[KeyedColumnElement[Any]] = [c for c in self.left.c] + [ - c for c in self.right.c - ] - - self.primary_key.extend( # type: ignore - sqlutil.reduce_columns( - (c for c in columns if c.primary_key), self.onclause - ) - ) - self._columns._populate_separate_keys( - (col._tq_key_label, col) for col in columns - ) - self.foreign_keys.update( # type: ignore - itertools.chain(*[col.foreign_keys for col in columns]) - ) - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - # see Select._copy_internals() for similar concept - - # here we pre-clone "left" and "right" so that we can - # determine the new FROM clauses - all_the_froms = set( - itertools.chain( - _from_objects(self.left), - _from_objects(self.right), - ) - ) - - # run the clone on those. these will be placed in the - # cache used by the clone function - new_froms = {f: clone(f, **kw) for f in all_the_froms} - - # set up a special replace function that will replace for - # ColumnClause with parent table referring to those - # replaced FromClause objects - def replace( - obj: Union[BinaryExpression[Any], ColumnClause[Any]], - **kw: Any, - ) -> Optional[KeyedColumnElement[ColumnElement[Any]]]: - if isinstance(obj, ColumnClause) and obj.table in new_froms: - newelem = new_froms[obj.table].corresponding_column(obj) - return newelem - return None - - kw["replace"] = replace - - # run normal _copy_internals. the clones for - # left and right will come from the clone function's - # cache - super()._copy_internals(clone=clone, **kw) - - self._reset_memoizations() - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - self.left._refresh_for_new_column(column) - self.right._refresh_for_new_column(column) - - def _match_primaries( - self, - left: FromClause, - right: FromClause, - ) -> ColumnElement[bool]: - if isinstance(left, Join): - left_right = left.right - else: - left_right = None - return self._join_condition(left, right, a_subset=left_right) - - @classmethod - def _join_condition( - cls, - a: FromClause, - b: FromClause, - *, - a_subset: Optional[FromClause] = None, - consider_as_foreign_keys: Optional[ - AbstractSet[ColumnClause[Any]] - ] = None, - ) -> ColumnElement[bool]: - """Create a join condition between two tables or selectables. - - See sqlalchemy.sql.util.join_condition() for full docs. - - """ - constraints = cls._joincond_scan_left_right( - a, a_subset, b, consider_as_foreign_keys - ) - - if len(constraints) > 1: - cls._joincond_trim_constraints( - a, b, constraints, consider_as_foreign_keys - ) - - if len(constraints) == 0: - if isinstance(b, FromGrouping): - hint = ( - " Perhaps you meant to convert the right side to a " - "subquery using alias()?" - ) - else: - hint = "" - raise exc.NoForeignKeysError( - "Can't find any foreign key relationships " - "between '%s' and '%s'.%s" - % (a.description, b.description, hint) - ) - - crit = [(x == y) for x, y in list(constraints.values())[0]] - if len(crit) == 1: - return crit[0] - else: - return and_(*crit) - - @classmethod - def _can_join( - cls, - left: FromClause, - right: FromClause, - *, - consider_as_foreign_keys: Optional[ - AbstractSet[ColumnClause[Any]] - ] = None, - ) -> bool: - if isinstance(left, Join): - left_right = left.right - else: - left_right = None - - constraints = cls._joincond_scan_left_right( - a=left, - b=right, - a_subset=left_right, - consider_as_foreign_keys=consider_as_foreign_keys, - ) - - return bool(constraints) - - @classmethod - @util.preload_module("sqlalchemy.sql.util") - def _joincond_scan_left_right( - cls, - a: FromClause, - a_subset: Optional[FromClause], - b: FromClause, - consider_as_foreign_keys: Optional[AbstractSet[ColumnClause[Any]]], - ) -> collections.defaultdict[ - Optional[ForeignKeyConstraint], - List[Tuple[ColumnClause[Any], ColumnClause[Any]]], - ]: - sql_util = util.preloaded.sql_util - - a = coercions.expect(roles.FromClauseRole, a) - b = coercions.expect(roles.FromClauseRole, b) - - constraints: collections.defaultdict[ - Optional[ForeignKeyConstraint], - List[Tuple[ColumnClause[Any], ColumnClause[Any]]], - ] = collections.defaultdict(list) - - for left in (a_subset, a): - if left is None: - continue - for fk in sorted( - b.foreign_keys, - key=lambda fk: fk.parent._creation_order, - ): - if ( - consider_as_foreign_keys is not None - and fk.parent not in consider_as_foreign_keys - ): - continue - try: - col = fk.get_referent(left) - except exc.NoReferenceError as nrte: - table_names = {t.name for t in sql_util.find_tables(left)} - if nrte.table_name in table_names: - raise - else: - continue - - if col is not None: - constraints[fk.constraint].append((col, fk.parent)) - if left is not b: - for fk in sorted( - left.foreign_keys, - key=lambda fk: fk.parent._creation_order, - ): - if ( - consider_as_foreign_keys is not None - and fk.parent not in consider_as_foreign_keys - ): - continue - try: - col = fk.get_referent(b) - except exc.NoReferenceError as nrte: - table_names = {t.name for t in sql_util.find_tables(b)} - if nrte.table_name in table_names: - raise - else: - continue - - if col is not None: - constraints[fk.constraint].append((col, fk.parent)) - if constraints: - break - return constraints - - @classmethod - def _joincond_trim_constraints( - cls, - a: FromClause, - b: FromClause, - constraints: Dict[Any, Any], - consider_as_foreign_keys: Optional[Any], - ) -> None: - # more than one constraint matched. narrow down the list - # to include just those FKCs that match exactly to - # "consider_as_foreign_keys". - if consider_as_foreign_keys: - for const in list(constraints): - if {f.parent for f in const.elements} != set( - consider_as_foreign_keys - ): - del constraints[const] - - # if still multiple constraints, but - # they all refer to the exact same end result, use it. - if len(constraints) > 1: - dedupe = {tuple(crit) for crit in constraints.values()} - if len(dedupe) == 1: - key = list(constraints)[0] - constraints = {key: constraints[key]} - - if len(constraints) != 1: - raise exc.AmbiguousForeignKeysError( - "Can't determine join between '%s' and '%s'; " - "tables have more than one foreign key " - "constraint relationship between them. " - "Please specify the 'onclause' of this " - "join explicitly." % (a.description, b.description) - ) - - def select(self) -> Select[Any]: - r"""Create a :class:`_expression.Select` from this - :class:`_expression.Join`. - - E.g.:: - - stmt = table_a.join(table_b, table_a.c.id == table_b.c.a_id) - - stmt = stmt.select() - - The above will produce a SQL string resembling:: - - SELECT table_a.id, table_a.col, table_b.id, table_b.a_id - FROM table_a JOIN table_b ON table_a.id = table_b.a_id - - """ - return Select(self.left, self.right).select_from(self) - - @util.preload_module("sqlalchemy.sql.util") - def _anonymous_fromclause( - self, name: Optional[str] = None, flat: bool = False - ) -> TODO_Any: - sqlutil = util.preloaded.sql_util - if flat: - if name is not None: - raise exc.ArgumentError("Can't send name argument with flat") - left_a, right_a = ( - self.left._anonymous_fromclause(flat=True), - self.right._anonymous_fromclause(flat=True), - ) - adapter = sqlutil.ClauseAdapter(left_a).chain( - sqlutil.ClauseAdapter(right_a) - ) - - return left_a.join( - right_a, - adapter.traverse(self.onclause), - isouter=self.isouter, - full=self.full, - ) - else: - return ( - self.select() - .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) - .correlate(None) - .alias(name) - ) - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return itertools.chain( - *[_from_objects(x.left, x.right) for x in self._cloned_set] - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - self_list: List[FromClause] = [self] - return self_list + self.left._from_objects + self.right._from_objects - - -class NoInit: - def __init__(self, *arg: Any, **kw: Any): - raise NotImplementedError( - "The %s class is not intended to be constructed " - "directly. Please use the %s() standalone " - "function or the %s() method available from appropriate " - "selectable objects." - % ( - self.__class__.__name__, - self.__class__.__name__.lower(), - self.__class__.__name__.lower(), - ) - ) - - -class LateralFromClause(NamedFromClause): - """mark a FROM clause as being able to render directly as LATERAL""" - - -# FromClause -> -# AliasedReturnsRows -# -> Alias only for FromClause -# -> Subquery only for SelectBase -# -> CTE only for HasCTE -> SelectBase, DML -# -> Lateral -> FromClause, but we accept SelectBase -# w/ non-deprecated coercion -# -> TableSample -> only for FromClause - - -class AliasedReturnsRows(NoInit, NamedFromClause): - """Base class of aliases against tables, subqueries, and other - selectables.""" - - _is_from_container = True - - _supports_derived_columns = False - - element: ReturnsRows - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("name", InternalTraversal.dp_anon_name), - ] - - @classmethod - def _construct( - cls, - selectable: Any, - *, - name: Optional[str] = None, - **kw: Any, - ) -> Self: - obj = cls.__new__(cls) - obj._init(selectable, name=name, **kw) - return obj - - def _init(self, selectable: Any, *, name: Optional[str] = None) -> None: - self.element = coercions.expect( - roles.ReturnsRowsRole, selectable, apply_propagate_attrs=self - ) - self.element = selectable - self._orig_name = name - if name is None: - if ( - isinstance(selectable, FromClause) - and selectable.named_with_column - ): - name = getattr(selectable, "name", None) - if isinstance(name, _anonymous_label): - name = None - name = _anonymous_label.safe_construct(id(self), name or "anon") - self.name = name - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - self.element._refresh_for_new_column(column) - - def _populate_column_collection(self) -> None: - self.element._generate_fromclause_column_proxies(self) - - @util.ro_non_memoized_property - def description(self) -> str: - name = self.name - if isinstance(name, _anonymous_label): - name = "anon_1" - - return name - - @util.ro_non_memoized_property - def implicit_returning(self) -> bool: - return self.element.implicit_returning # type: ignore - - @property - def original(self) -> ReturnsRows: - """Legacy for dialects that are referring to Alias.original.""" - return self.element - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - if fromclause in self._cloned_set: - return True - return self.element.is_derived_from(fromclause) - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - existing_element = self.element - - super()._copy_internals(clone=clone, **kw) - - # the element clone is usually against a Table that returns the - # same object. don't reset exported .c. collections and other - # memoized details if it was not changed. this saves a lot on - # performance. - if existing_element is not self.element: - self._reset_column_collection() - - @property - def _from_objects(self) -> List[FromClause]: - return [self] - - -class FromClauseAlias(AliasedReturnsRows): - element: FromClause - - -class Alias(roles.DMLTableRole, FromClauseAlias): - """Represents an table or selectable alias (AS). - - Represents an alias, as typically applied to any table or - sub-select within a SQL statement using the ``AS`` keyword (or - without the keyword on certain databases such as Oracle). - - This object is constructed from the :func:`_expression.alias` module - level function as well as the :meth:`_expression.FromClause.alias` - method available - on all :class:`_expression.FromClause` subclasses. - - .. seealso:: - - :meth:`_expression.FromClause.alias` - - """ - - __visit_name__ = "alias" - - inherit_cache = True - - element: FromClause - - @classmethod - def _factory( - cls, - selectable: FromClause, - name: Optional[str] = None, - flat: bool = False, - ) -> NamedFromClause: - return coercions.expect( - roles.FromClauseRole, selectable, allow_select=True - ).alias(name=name, flat=flat) - - -class TableValuedAlias(LateralFromClause, Alias): - """An alias against a "table valued" SQL function. - - This construct provides for a SQL function that returns columns - to be used in the FROM clause of a SELECT statement. The - object is generated using the :meth:`_functions.FunctionElement.table_valued` - method, e.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import select, func - >>> fn = func.json_array_elements_text('["one", "two", "three"]').table_valued("value") - >>> print(select(fn.c.value)) - {printsql}SELECT anon_1.value - FROM json_array_elements_text(:json_array_elements_text_1) AS anon_1 - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :ref:`tutorial_functions_table_valued` - in the :ref:`unified_tutorial` - - """ # noqa: E501 - - __visit_name__ = "table_valued_alias" - - _supports_derived_columns = True - _render_derived = False - _render_derived_w_types = False - joins_implicitly = False - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("name", InternalTraversal.dp_anon_name), - ("_tableval_type", InternalTraversal.dp_type), - ("_render_derived", InternalTraversal.dp_boolean), - ("_render_derived_w_types", InternalTraversal.dp_boolean), - ] - - def _init( - self, - selectable: Any, - *, - name: Optional[str] = None, - table_value_type: Optional[TableValueType] = None, - joins_implicitly: bool = False, - ) -> None: - super()._init(selectable, name=name) - - self.joins_implicitly = joins_implicitly - self._tableval_type = ( - type_api.TABLEVALUE - if table_value_type is None - else table_value_type - ) - - @HasMemoized.memoized_attribute - def column(self) -> TableValuedColumn[Any]: - """Return a column expression representing this - :class:`_sql.TableValuedAlias`. - - This accessor is used to implement the - :meth:`_functions.FunctionElement.column_valued` method. See that - method for further details. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> print(select(func.some_func().table_valued("value").column)) - {printsql}SELECT anon_1 FROM some_func() AS anon_1 - - .. seealso:: - - :meth:`_functions.FunctionElement.column_valued` - - """ - - return TableValuedColumn(self, self._tableval_type) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> TableValuedAlias: - """Return a new alias of this :class:`_sql.TableValuedAlias`. - - This creates a distinct FROM object that will be distinguished - from the original one when used in a SQL statement. - - """ - - tva: TableValuedAlias = TableValuedAlias._construct( - self, - name=name, - table_value_type=self._tableval_type, - joins_implicitly=self.joins_implicitly, - ) - - if self._render_derived: - tva._render_derived = True - tva._render_derived_w_types = self._render_derived_w_types - - return tva - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a new :class:`_sql.TableValuedAlias` with the lateral flag - set, so that it renders as LATERAL. - - .. seealso:: - - :func:`_expression.lateral` - - """ - tva = self.alias(name=name) - tva._is_lateral = True - return tva - - def render_derived( - self, - name: Optional[str] = None, - with_types: bool = False, - ) -> TableValuedAlias: - """Apply "render derived" to this :class:`_sql.TableValuedAlias`. - - This has the effect of the individual column names listed out - after the alias name in the "AS" sequence, e.g.: - - .. sourcecode:: pycon+sql - - >>> print( - ... select( - ... func.unnest(array(["one", "two", "three"])). - table_valued("x", with_ordinality="o").render_derived() - ... ) - ... ) - {printsql}SELECT anon_1.x, anon_1.o - FROM unnest(ARRAY[%(param_1)s, %(param_2)s, %(param_3)s]) WITH ORDINALITY AS anon_1(x, o) - - The ``with_types`` keyword will render column types inline within - the alias expression (this syntax currently applies to the - PostgreSQL database): - - .. sourcecode:: pycon+sql - - >>> print( - ... select( - ... func.json_to_recordset( - ... '[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]' - ... ) - ... .table_valued(column("a", Integer), column("b", String)) - ... .render_derived(with_types=True) - ... ) - ... ) - {printsql}SELECT anon_1.a, anon_1.b FROM json_to_recordset(:json_to_recordset_1) - AS anon_1(a INTEGER, b VARCHAR) - - :param name: optional string name that will be applied to the alias - generated. If left as None, a unique anonymizing name will be used. - - :param with_types: if True, the derived columns will include the - datatype specification with each column. This is a special syntax - currently known to be required by PostgreSQL for some SQL functions. - - """ # noqa: E501 - - # note: don't use the @_generative system here, keep a reference - # to the original object. otherwise you can have re-use of the - # python id() of the original which can cause name conflicts if - # a new anon-name grabs the same identifier as the local anon-name - # (just saw it happen on CI) - - # construct against original to prevent memory growth - # for repeated generations - new_alias: TableValuedAlias = TableValuedAlias._construct( - self.element, - name=name, - table_value_type=self._tableval_type, - joins_implicitly=self.joins_implicitly, - ) - new_alias._render_derived = True - new_alias._render_derived_w_types = with_types - return new_alias - - -class Lateral(FromClauseAlias, LateralFromClause): - """Represent a LATERAL subquery. - - This object is constructed from the :func:`_expression.lateral` module - level function as well as the :meth:`_expression.FromClause.lateral` - method available - on all :class:`_expression.FromClause` subclasses. - - While LATERAL is part of the SQL standard, currently only more recent - PostgreSQL versions provide support for this keyword. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - - __visit_name__ = "lateral" - _is_lateral = True - - inherit_cache = True - - @classmethod - def _factory( - cls, - selectable: Union[SelectBase, _FromClauseArgument], - name: Optional[str] = None, - ) -> LateralFromClause: - return coercions.expect( - roles.FromClauseRole, selectable, explicit_subquery=True - ).lateral(name=name) - - -class TableSample(FromClauseAlias): - """Represent a TABLESAMPLE clause. - - This object is constructed from the :func:`_expression.tablesample` module - level function as well as the :meth:`_expression.FromClause.tablesample` - method - available on all :class:`_expression.FromClause` subclasses. - - .. seealso:: - - :func:`_expression.tablesample` - - """ - - __visit_name__ = "tablesample" - - _traverse_internals: _TraverseInternalsType = ( - AliasedReturnsRows._traverse_internals - + [ - ("sampling", InternalTraversal.dp_clauseelement), - ("seed", InternalTraversal.dp_clauseelement), - ] - ) - - @classmethod - def _factory( - cls, - selectable: _FromClauseArgument, - sampling: Union[float, Function[Any]], - name: Optional[str] = None, - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> TableSample: - return coercions.expect(roles.FromClauseRole, selectable).tablesample( - sampling, name=name, seed=seed - ) - - @util.preload_module("sqlalchemy.sql.functions") - def _init( # type: ignore[override] - self, - selectable: Any, - *, - name: Optional[str] = None, - sampling: Union[float, Function[Any]], - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> None: - assert sampling is not None - functions = util.preloaded.sql_functions - if not isinstance(sampling, functions.Function): - sampling = functions.func.system(sampling) - - self.sampling: Function[Any] = sampling - self.seed = seed - super()._init(selectable, name=name) - - def _get_method(self) -> Function[Any]: - return self.sampling - - -class CTE( - roles.DMLTableRole, - roles.IsCTERole, - Generative, - HasPrefixes, - HasSuffixes, - AliasedReturnsRows, -): - """Represent a Common Table Expression. - - The :class:`_expression.CTE` object is obtained using the - :meth:`_sql.SelectBase.cte` method from any SELECT statement. A less often - available syntax also allows use of the :meth:`_sql.HasCTE.cte` method - present on :term:`DML` constructs such as :class:`_sql.Insert`, - :class:`_sql.Update` and - :class:`_sql.Delete`. See the :meth:`_sql.HasCTE.cte` method for - usage details on CTEs. - - .. seealso:: - - :ref:`tutorial_subqueries_ctes` - in the 2.0 tutorial - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - - __visit_name__ = "cte" - - _traverse_internals: _TraverseInternalsType = ( - AliasedReturnsRows._traverse_internals - + [ - ("_cte_alias", InternalTraversal.dp_clauseelement), - ("_restates", InternalTraversal.dp_clauseelement), - ("recursive", InternalTraversal.dp_boolean), - ("nesting", InternalTraversal.dp_boolean), - ] - + HasPrefixes._has_prefixes_traverse_internals - + HasSuffixes._has_suffixes_traverse_internals - ) - - element: HasCTE - - @classmethod - def _factory( - cls, - selectable: HasCTE, - name: Optional[str] = None, - recursive: bool = False, - ) -> CTE: - r"""Return a new :class:`_expression.CTE`, - or Common Table Expression instance. - - Please see :meth:`_expression.HasCTE.cte` for detail on CTE usage. - - """ - return coercions.expect(roles.HasCTERole, selectable).cte( - name=name, recursive=recursive - ) - - def _init( - self, - selectable: Select[Any], - *, - name: Optional[str] = None, - recursive: bool = False, - nesting: bool = False, - _cte_alias: Optional[CTE] = None, - _restates: Optional[CTE] = None, - _prefixes: Optional[Tuple[()]] = None, - _suffixes: Optional[Tuple[()]] = None, - ) -> None: - self.recursive = recursive - self.nesting = nesting - self._cte_alias = _cte_alias - # Keep recursivity reference with union/union_all - self._restates = _restates - if _prefixes: - self._prefixes = _prefixes - if _suffixes: - self._suffixes = _suffixes - super()._init(selectable, name=name) - - def _populate_column_collection(self) -> None: - if self._cte_alias is not None: - self._cte_alias._generate_fromclause_column_proxies(self) - else: - self.element._generate_fromclause_column_proxies(self) - - def alias(self, name: Optional[str] = None, flat: bool = False) -> CTE: - """Return an :class:`_expression.Alias` of this - :class:`_expression.CTE`. - - This method is a CTE-specific specialization of the - :meth:`_expression.FromClause.alias` method. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - return CTE._construct( - self.element, - name=name, - recursive=self.recursive, - nesting=self.nesting, - _cte_alias=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def union(self, *other: _SelectStatementForCompoundArgument) -> CTE: - r"""Return a new :class:`_expression.CTE` with a SQL ``UNION`` - of the original CTE against the given selectables provided - as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 multiple elements are now accepted. - - .. seealso:: - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - assert is_select_statement( - self.element - ), f"CTE element f{self.element} does not support union()" - - return CTE._construct( - self.element.union(*other), - name=self.name, - recursive=self.recursive, - nesting=self.nesting, - _restates=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def union_all(self, *other: _SelectStatementForCompoundArgument) -> CTE: - r"""Return a new :class:`_expression.CTE` with a SQL ``UNION ALL`` - of the original CTE against the given selectables provided - as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 multiple elements are now accepted. - - .. seealso:: - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - - assert is_select_statement( - self.element - ), f"CTE element f{self.element} does not support union_all()" - - return CTE._construct( - self.element.union_all(*other), - name=self.name, - recursive=self.recursive, - nesting=self.nesting, - _restates=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def _get_reference_cte(self) -> CTE: - """ - A recursive CTE is updated to attach the recursive part. - Updated CTEs should still refer to the original CTE. - This function returns this reference identifier. - """ - return self._restates if self._restates is not None else self - - -class _CTEOpts(NamedTuple): - nesting: bool - - -class _ColumnsPlusNames(NamedTuple): - required_label_name: Optional[str] - """ - string label name, if non-None, must be rendered as a - label, i.e. "AS <name>" - """ - - proxy_key: Optional[str] - """ - proxy_key that is to be part of the result map for this - col. this is also the key in a fromclause.c or - select.selected_columns collection - """ - - fallback_label_name: Optional[str] - """ - name that can be used to render an "AS <name>" when - we have to render a label even though - required_label_name was not given - """ - - column: Union[ColumnElement[Any], TextClause] - """ - the ColumnElement itself - """ - - repeated: bool - """ - True if this is a duplicate of a previous column - in the list of columns - """ - - -class SelectsRows(ReturnsRows): - """Sub-base of ReturnsRows for elements that deliver rows - directly, namely SELECT and INSERT/UPDATE/DELETE..RETURNING""" - - _label_style: SelectLabelStyle = LABEL_STYLE_NONE - - def _generate_columns_plus_names( - self, - anon_for_dupe_key: bool, - cols: Optional[_SelectIterable] = None, - ) -> List[_ColumnsPlusNames]: - """Generate column names as rendered in a SELECT statement by - the compiler. - - This is distinct from the _column_naming_convention generator that's - intended for population of .c collections and similar, which has - different rules. the collection returned here calls upon the - _column_naming_convention as well. - - """ - - if cols is None: - cols = self._all_selected_columns - - key_naming_convention = SelectState._column_naming_convention( - self._label_style - ) - - names = {} - - result: List[_ColumnsPlusNames] = [] - result_append = result.append - - table_qualified = self._label_style is LABEL_STYLE_TABLENAME_PLUS_COL - label_style_none = self._label_style is LABEL_STYLE_NONE - - # a counter used for "dedupe" labels, which have double underscores - # in them and are never referred by name; they only act - # as positional placeholders. they need only be unique within - # the single columns clause they're rendered within (required by - # some dbs such as mysql). So their anon identity is tracked against - # a fixed counter rather than hash() identity. - dedupe_hash = 1 - - for c in cols: - repeated = False - - if not c._render_label_in_columns_clause: - effective_name = required_label_name = fallback_label_name = ( - None - ) - elif label_style_none: - if TYPE_CHECKING: - assert is_column_element(c) - - effective_name = required_label_name = None - fallback_label_name = c._non_anon_label or c._anon_name_label - else: - if TYPE_CHECKING: - assert is_column_element(c) - - if table_qualified: - required_label_name = effective_name = ( - fallback_label_name - ) = c._tq_label - else: - effective_name = fallback_label_name = c._non_anon_label - required_label_name = None - - if effective_name is None: - # it seems like this could be _proxy_key and we would - # not need _expression_label but it isn't - # giving us a clue when to use anon_label instead - expr_label = c._expression_label - if expr_label is None: - repeated = c._anon_name_label in names - names[c._anon_name_label] = c - effective_name = required_label_name = None - - if repeated: - # here, "required_label_name" is sent as - # "None" and "fallback_label_name" is sent. - if table_qualified: - fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - fallback_label_name = c._dedupe_anon_label_idx( - dedupe_hash - ) - dedupe_hash += 1 - else: - fallback_label_name = c._anon_name_label - else: - required_label_name = effective_name = ( - fallback_label_name - ) = expr_label - - if effective_name is not None: - if TYPE_CHECKING: - assert is_column_element(c) - - if effective_name in names: - # when looking to see if names[name] is the same column as - # c, use hash(), so that an annotated version of the column - # is seen as the same as the non-annotated - if hash(names[effective_name]) != hash(c): - # different column under the same name. apply - # disambiguating label - if table_qualified: - required_label_name = fallback_label_name = ( - c._anon_tq_label - ) - else: - required_label_name = fallback_label_name = ( - c._anon_name_label - ) - - if anon_for_dupe_key and required_label_name in names: - # here, c._anon_tq_label is definitely unique to - # that column identity (or annotated version), so - # this should always be true. - # this is also an infrequent codepath because - # you need two levels of duplication to be here - assert hash(names[required_label_name]) == hash(c) - - # the column under the disambiguating label is - # already present. apply the "dedupe" label to - # subsequent occurrences of the column so that the - # original stays non-ambiguous - if table_qualified: - required_label_name = fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - required_label_name = fallback_label_name = ( - c._dedupe_anon_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - repeated = True - else: - names[required_label_name] = c - elif anon_for_dupe_key: - # same column under the same name. apply the "dedupe" - # label so that the original stays non-ambiguous - if table_qualified: - required_label_name = fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - required_label_name = fallback_label_name = ( - c._dedupe_anon_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - repeated = True - else: - names[effective_name] = c - - result_append( - _ColumnsPlusNames( - required_label_name, - key_naming_convention(c), - fallback_label_name, - c, - repeated, - ) - ) - - return result - - -class HasCTE(roles.HasCTERole, SelectsRows): - """Mixin that declares a class to include CTE support.""" - - _has_ctes_traverse_internals: _TraverseInternalsType = [ - ("_independent_ctes", InternalTraversal.dp_clauseelement_list), - ("_independent_ctes_opts", InternalTraversal.dp_plain_obj), - ] - - _independent_ctes: Tuple[CTE, ...] = () - _independent_ctes_opts: Tuple[_CTEOpts, ...] = () - - @_generative - def add_cte(self, *ctes: CTE, nest_here: bool = False) -> Self: - r"""Add one or more :class:`_sql.CTE` constructs to this statement. - - This method will associate the given :class:`_sql.CTE` constructs with - the parent statement such that they will each be unconditionally - rendered in the WITH clause of the final statement, even if not - referenced elsewhere within the statement or any sub-selects. - - The optional :paramref:`.HasCTE.add_cte.nest_here` parameter when set - to True will have the effect that each given :class:`_sql.CTE` will - render in a WITH clause rendered directly along with this statement, - rather than being moved to the top of the ultimate rendered statement, - even if this statement is rendered as a subquery within a larger - statement. - - This method has two general uses. One is to embed CTE statements that - serve some purpose without being referenced explicitly, such as the use - case of embedding a DML statement such as an INSERT or UPDATE as a CTE - inline with a primary statement that may draw from its results - indirectly. The other is to provide control over the exact placement - of a particular series of CTE constructs that should remain rendered - directly in terms of a particular statement that may be nested in a - larger statement. - - E.g.:: - - from sqlalchemy import table, column, select - t = table('t', column('c1'), column('c2')) - - ins = t.insert().values({"c1": "x", "c2": "y"}).cte() - - stmt = select(t).add_cte(ins) - - Would render:: - - WITH anon_1 AS - (INSERT INTO t (c1, c2) VALUES (:param_1, :param_2)) - SELECT t.c1, t.c2 - FROM t - - Above, the "anon_1" CTE is not referenced in the SELECT - statement, however still accomplishes the task of running an INSERT - statement. - - Similarly in a DML-related context, using the PostgreSQL - :class:`_postgresql.Insert` construct to generate an "upsert":: - - from sqlalchemy import table, column - from sqlalchemy.dialects.postgresql import insert - - t = table("t", column("c1"), column("c2")) - - delete_statement_cte = ( - t.delete().where(t.c.c1 < 1).cte("deletions") - ) - - insert_stmt = insert(t).values({"c1": 1, "c2": 2}) - update_statement = insert_stmt.on_conflict_do_update( - index_elements=[t.c.c1], - set_={ - "c1": insert_stmt.excluded.c1, - "c2": insert_stmt.excluded.c2, - }, - ).add_cte(delete_statement_cte) - - print(update_statement) - - The above statement renders as:: - - WITH deletions AS - (DELETE FROM t WHERE t.c1 < %(c1_1)s) - INSERT INTO t (c1, c2) VALUES (%(c1)s, %(c2)s) - ON CONFLICT (c1) DO UPDATE SET c1 = excluded.c1, c2 = excluded.c2 - - .. versionadded:: 1.4.21 - - :param \*ctes: zero or more :class:`.CTE` constructs. - - .. versionchanged:: 2.0 Multiple CTE instances are accepted - - :param nest_here: if True, the given CTE or CTEs will be rendered - as though they specified the :paramref:`.HasCTE.cte.nesting` flag - to ``True`` when they were added to this :class:`.HasCTE`. - Assuming the given CTEs are not referenced in an outer-enclosing - statement as well, the CTEs given should render at the level of - this statement when this flag is given. - - .. versionadded:: 2.0 - - .. seealso:: - - :paramref:`.HasCTE.cte.nesting` - - - """ - opt = _CTEOpts( - nest_here, - ) - for cte in ctes: - cte = coercions.expect(roles.IsCTERole, cte) - self._independent_ctes += (cte,) - self._independent_ctes_opts += (opt,) - return self - - def cte( - self, - name: Optional[str] = None, - recursive: bool = False, - nesting: bool = False, - ) -> CTE: - r"""Return a new :class:`_expression.CTE`, - or Common Table Expression instance. - - Common table expressions are a SQL standard whereby SELECT - statements can draw upon secondary statements specified along - with the primary statement, using a clause called "WITH". - Special semantics regarding UNION can also be employed to - allow "recursive" queries, where a SELECT statement can draw - upon the set of rows that have previously been selected. - - CTEs can also be applied to DML constructs UPDATE, INSERT - and DELETE on some databases, both as a source of CTE rows - when combined with RETURNING, as well as a consumer of - CTE rows. - - SQLAlchemy detects :class:`_expression.CTE` objects, which are treated - similarly to :class:`_expression.Alias` objects, as special elements - to be delivered to the FROM clause of the statement as well - as to a WITH clause at the top of the statement. - - For special prefixes such as PostgreSQL "MATERIALIZED" and - "NOT MATERIALIZED", the :meth:`_expression.CTE.prefix_with` - method may be - used to establish these. - - .. versionchanged:: 1.3.13 Added support for prefixes. - In particular - MATERIALIZED and NOT MATERIALIZED. - - :param name: name given to the common table expression. Like - :meth:`_expression.FromClause.alias`, the name can be left as - ``None`` in which case an anonymous symbol will be used at query - compile time. - :param recursive: if ``True``, will render ``WITH RECURSIVE``. - A recursive common table expression is intended to be used in - conjunction with UNION ALL in order to derive rows - from those already selected. - :param nesting: if ``True``, will render the CTE locally to the - statement in which it is referenced. For more complex scenarios, - the :meth:`.HasCTE.add_cte` method using the - :paramref:`.HasCTE.add_cte.nest_here` - parameter may also be used to more carefully - control the exact placement of a particular CTE. - - .. versionadded:: 1.4.24 - - .. seealso:: - - :meth:`.HasCTE.add_cte` - - The following examples include two from PostgreSQL's documentation at - https://www.postgresql.org/docs/current/static/queries-with.html, - as well as additional examples. - - Example 1, non recursive:: - - from sqlalchemy import (Table, Column, String, Integer, - MetaData, select, func) - - metadata = MetaData() - - orders = Table('orders', metadata, - Column('region', String), - Column('amount', Integer), - Column('product', String), - Column('quantity', Integer) - ) - - regional_sales = select( - orders.c.region, - func.sum(orders.c.amount).label('total_sales') - ).group_by(orders.c.region).cte("regional_sales") - - - top_regions = select(regional_sales.c.region).\ - where( - regional_sales.c.total_sales > - select( - func.sum(regional_sales.c.total_sales) / 10 - ) - ).cte("top_regions") - - statement = select( - orders.c.region, - orders.c.product, - func.sum(orders.c.quantity).label("product_units"), - func.sum(orders.c.amount).label("product_sales") - ).where(orders.c.region.in_( - select(top_regions.c.region) - )).group_by(orders.c.region, orders.c.product) - - result = conn.execute(statement).fetchall() - - Example 2, WITH RECURSIVE:: - - from sqlalchemy import (Table, Column, String, Integer, - MetaData, select, func) - - metadata = MetaData() - - parts = Table('parts', metadata, - Column('part', String), - Column('sub_part', String), - Column('quantity', Integer), - ) - - included_parts = select(\ - parts.c.sub_part, parts.c.part, parts.c.quantity\ - ).\ - where(parts.c.part=='our part').\ - cte(recursive=True) - - - incl_alias = included_parts.alias() - parts_alias = parts.alias() - included_parts = included_parts.union_all( - select( - parts_alias.c.sub_part, - parts_alias.c.part, - parts_alias.c.quantity - ).\ - where(parts_alias.c.part==incl_alias.c.sub_part) - ) - - statement = select( - included_parts.c.sub_part, - func.sum(included_parts.c.quantity). - label('total_quantity') - ).\ - group_by(included_parts.c.sub_part) - - result = conn.execute(statement).fetchall() - - Example 3, an upsert using UPDATE and INSERT with CTEs:: - - from datetime import date - from sqlalchemy import (MetaData, Table, Column, Integer, - Date, select, literal, and_, exists) - - metadata = MetaData() - - visitors = Table('visitors', metadata, - Column('product_id', Integer, primary_key=True), - Column('date', Date, primary_key=True), - Column('count', Integer), - ) - - # add 5 visitors for the product_id == 1 - product_id = 1 - day = date.today() - count = 5 - - update_cte = ( - visitors.update() - .where(and_(visitors.c.product_id == product_id, - visitors.c.date == day)) - .values(count=visitors.c.count + count) - .returning(literal(1)) - .cte('update_cte') - ) - - upsert = visitors.insert().from_select( - [visitors.c.product_id, visitors.c.date, visitors.c.count], - select(literal(product_id), literal(day), literal(count)) - .where(~exists(update_cte.select())) - ) - - connection.execute(upsert) - - Example 4, Nesting CTE (SQLAlchemy 1.4.24 and above):: - - value_a = select( - literal("root").label("n") - ).cte("value_a") - - # A nested CTE with the same name as the root one - value_a_nested = select( - literal("nesting").label("n") - ).cte("value_a", nesting=True) - - # Nesting CTEs takes ascendency locally - # over the CTEs at a higher level - value_b = select(value_a_nested.c.n).cte("value_b") - - value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b")) - - The above query will render the second CTE nested inside the first, - shown with inline parameters below as:: - - WITH - value_a AS - (SELECT 'root' AS n), - value_b AS - (WITH value_a AS - (SELECT 'nesting' AS n) - SELECT value_a.n AS n FROM value_a) - SELECT value_a.n AS a, value_b.n AS b - FROM value_a, value_b - - The same CTE can be set up using the :meth:`.HasCTE.add_cte` method - as follows (SQLAlchemy 2.0 and above):: - - value_a = select( - literal("root").label("n") - ).cte("value_a") - - # A nested CTE with the same name as the root one - value_a_nested = select( - literal("nesting").label("n") - ).cte("value_a") - - # Nesting CTEs takes ascendency locally - # over the CTEs at a higher level - value_b = ( - select(value_a_nested.c.n). - add_cte(value_a_nested, nest_here=True). - cte("value_b") - ) - - value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b")) - - Example 5, Non-Linear CTE (SQLAlchemy 1.4.28 and above):: - - edge = Table( - "edge", - metadata, - Column("id", Integer, primary_key=True), - Column("left", Integer), - Column("right", Integer), - ) - - root_node = select(literal(1).label("node")).cte( - "nodes", recursive=True - ) - - left_edge = select(edge.c.left).join( - root_node, edge.c.right == root_node.c.node - ) - right_edge = select(edge.c.right).join( - root_node, edge.c.left == root_node.c.node - ) - - subgraph_cte = root_node.union(left_edge, right_edge) - - subgraph = select(subgraph_cte) - - The above query will render 2 UNIONs inside the recursive CTE:: - - WITH RECURSIVE nodes(node) AS ( - SELECT 1 AS node - UNION - SELECT edge."left" AS "left" - FROM edge JOIN nodes ON edge."right" = nodes.node - UNION - SELECT edge."right" AS "right" - FROM edge JOIN nodes ON edge."left" = nodes.node - ) - SELECT nodes.node FROM nodes - - .. seealso:: - - :meth:`_orm.Query.cte` - ORM version of - :meth:`_expression.HasCTE.cte`. - - """ - return CTE._construct( - self, name=name, recursive=recursive, nesting=nesting - ) - - -class Subquery(AliasedReturnsRows): - """Represent a subquery of a SELECT. - - A :class:`.Subquery` is created by invoking the - :meth:`_expression.SelectBase.subquery` method, or for convenience the - :meth:`_expression.SelectBase.alias` method, on any - :class:`_expression.SelectBase` subclass - which includes :class:`_expression.Select`, - :class:`_expression.CompoundSelect`, and - :class:`_expression.TextualSelect`. As rendered in a FROM clause, - it represents the - body of the SELECT statement inside of parenthesis, followed by the usual - "AS <somename>" that defines all "alias" objects. - - The :class:`.Subquery` object is very similar to the - :class:`_expression.Alias` - object and can be used in an equivalent way. The difference between - :class:`_expression.Alias` and :class:`.Subquery` is that - :class:`_expression.Alias` always - contains a :class:`_expression.FromClause` object whereas - :class:`.Subquery` - always contains a :class:`_expression.SelectBase` object. - - .. versionadded:: 1.4 The :class:`.Subquery` class was added which now - serves the purpose of providing an aliased version of a SELECT - statement. - - """ - - __visit_name__ = "subquery" - - _is_subquery = True - - inherit_cache = True - - element: SelectBase - - @classmethod - def _factory( - cls, selectable: SelectBase, name: Optional[str] = None - ) -> Subquery: - """Return a :class:`.Subquery` object.""" - - return coercions.expect( - roles.SelectStatementRole, selectable - ).subquery(name=name) - - @util.deprecated( - "1.4", - "The :meth:`.Subquery.as_scalar` method, which was previously " - "``Alias.as_scalar()`` prior to version 1.4, is deprecated and " - "will be removed in a future release; Please use the " - ":meth:`_expression.Select.scalar_subquery` method of the " - ":func:`_expression.select` " - "construct before constructing a subquery object, or with the ORM " - "use the :meth:`_query.Query.scalar_subquery` method.", - ) - def as_scalar(self) -> ScalarSelect[Any]: - return self.element.set_label_style(LABEL_STYLE_NONE).scalar_subquery() - - -class FromGrouping(GroupedElement, FromClause): - """Represent a grouping of a FROM clause""" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement) - ] - - element: FromClause - - def __init__(self, element: FromClause): - self.element = coercions.expect(roles.FromClauseRole, element) - - def _init_collections(self) -> None: - pass - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.element.columns - - @util.ro_non_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.element.columns - - @property - def primary_key(self) -> Iterable[NamedColumn[Any]]: - return self.element.primary_key - - @property - def foreign_keys(self) -> Iterable[ForeignKey]: - return self.element.foreign_keys - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - return self.element.is_derived_from(fromclause) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> NamedFromGrouping: - return NamedFromGrouping(self.element.alias(name=name, flat=flat)) - - def _anonymous_fromclause(self, **kw: Any) -> FromGrouping: - return FromGrouping(self.element._anonymous_fromclause(**kw)) - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return self.element._hide_froms - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - def __getstate__(self) -> Dict[str, FromClause]: - return {"element": self.element} - - def __setstate__(self, state: Dict[str, FromClause]) -> None: - self.element = state["element"] - - -class NamedFromGrouping(FromGrouping, NamedFromClause): - """represent a grouping of a named FROM clause - - .. versionadded:: 2.0 - - """ - - inherit_cache = True - - -class TableClause(roles.DMLTableRole, Immutable, NamedFromClause): - """Represents a minimal "table" construct. - - This is a lightweight table object that has only a name, a - collection of columns, which are typically produced - by the :func:`_expression.column` function, and a schema:: - - from sqlalchemy import table, column - - user = table("user", - column("id"), - column("name"), - column("description"), - ) - - The :class:`_expression.TableClause` construct serves as the base for - the more commonly used :class:`_schema.Table` object, providing - the usual set of :class:`_expression.FromClause` services including - the ``.c.`` collection and statement generation methods. - - It does **not** provide all the additional schema-level services - of :class:`_schema.Table`, including constraints, references to other - tables, or support for :class:`_schema.MetaData`-level services. - It's useful - on its own as an ad-hoc construct used to generate quick SQL - statements when a more fully fledged :class:`_schema.Table` - is not on hand. - - """ - - __visit_name__ = "table" - - _traverse_internals: _TraverseInternalsType = [ - ( - "columns", - InternalTraversal.dp_fromclause_canonical_column_collection, - ), - ("name", InternalTraversal.dp_string), - ("schema", InternalTraversal.dp_string), - ] - - _is_table = True - - fullname: str - - implicit_returning = False - """:class:`_expression.TableClause` - doesn't support having a primary key or column - -level defaults, so implicit returning doesn't apply.""" - - @util.ro_memoized_property - def _autoincrement_column(self) -> Optional[ColumnClause[Any]]: - """No PK or default support so no autoincrement column.""" - return None - - def __init__(self, name: str, *columns: ColumnClause[Any], **kw: Any): - super().__init__() - self.name = name - self._columns = DedupeColumnCollection() - self.primary_key = ColumnSet() # type: ignore - self.foreign_keys = set() # type: ignore - for c in columns: - self.append_column(c) - - schema = kw.pop("schema", None) - if schema is not None: - self.schema = schema - if self.schema is not None: - self.fullname = "%s.%s" % (self.schema, self.name) - else: - self.fullname = self.name - if kw: - raise exc.ArgumentError("Unsupported argument(s): %s" % list(kw)) - - if TYPE_CHECKING: - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]: ... - - @util.ro_non_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]: ... - - def __str__(self) -> str: - if self.schema is not None: - return self.schema + "." + self.name - else: - return self.name - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - pass - - def _init_collections(self) -> None: - pass - - @util.ro_memoized_property - def description(self) -> str: - return self.name - - def append_column(self, c: ColumnClause[Any]) -> None: - existing = c.table - if existing is not None and existing is not self: - raise exc.ArgumentError( - "column object '%s' already assigned to table '%s'" - % (c.key, existing) - ) - - self._columns.add(c) - c.table = self - - @util.preload_module("sqlalchemy.sql.dml") - def insert(self) -> util.preloaded.sql_dml.Insert: - """Generate an :class:`_sql.Insert` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.insert().values(name='foo') - - See :func:`_expression.insert` for argument and usage information. - - """ - - return util.preloaded.sql_dml.Insert(self) - - @util.preload_module("sqlalchemy.sql.dml") - def update(self) -> Update: - """Generate an :func:`_expression.update` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.update().where(table.c.id==7).values(name='foo') - - See :func:`_expression.update` for argument and usage information. - - """ - return util.preloaded.sql_dml.Update( - self, - ) - - @util.preload_module("sqlalchemy.sql.dml") - def delete(self) -> Delete: - """Generate a :func:`_expression.delete` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.delete().where(table.c.id==7) - - See :func:`_expression.delete` for argument and usage information. - - """ - return util.preloaded.sql_dml.Delete(self) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [self] - - -ForUpdateParameter = Union["ForUpdateArg", None, bool, Dict[str, Any]] - - -class ForUpdateArg(ClauseElement): - _traverse_internals: _TraverseInternalsType = [ - ("of", InternalTraversal.dp_clauseelement_list), - ("nowait", InternalTraversal.dp_boolean), - ("read", InternalTraversal.dp_boolean), - ("skip_locked", InternalTraversal.dp_boolean), - ] - - of: Optional[Sequence[ClauseElement]] - nowait: bool - read: bool - skip_locked: bool - - @classmethod - def _from_argument( - cls, with_for_update: ForUpdateParameter - ) -> Optional[ForUpdateArg]: - if isinstance(with_for_update, ForUpdateArg): - return with_for_update - elif with_for_update in (None, False): - return None - elif with_for_update is True: - return ForUpdateArg() - else: - return ForUpdateArg(**cast("Dict[str, Any]", with_for_update)) - - def __eq__(self, other: Any) -> bool: - return ( - isinstance(other, ForUpdateArg) - and other.nowait == self.nowait - and other.read == self.read - and other.skip_locked == self.skip_locked - and other.key_share == self.key_share - and other.of is self.of - ) - - def __ne__(self, other: Any) -> bool: - return not self.__eq__(other) - - def __hash__(self) -> int: - return id(self) - - def __init__( - self, - *, - nowait: bool = False, - read: bool = False, - of: Optional[_ForUpdateOfArgument] = None, - skip_locked: bool = False, - key_share: bool = False, - ): - """Represents arguments specified to - :meth:`_expression.Select.for_update`. - - """ - - self.nowait = nowait - self.read = read - self.skip_locked = skip_locked - self.key_share = key_share - if of is not None: - self.of = [ - coercions.expect(roles.ColumnsClauseRole, elem) - for elem in util.to_list(of) - ] - else: - self.of = None - - -class Values(roles.InElementRole, Generative, LateralFromClause): - """Represent a ``VALUES`` construct that can be used as a FROM element - in a statement. - - The :class:`_expression.Values` object is created from the - :func:`_expression.values` function. - - .. versionadded:: 1.4 - - """ - - __visit_name__ = "values" - - _data: Tuple[Sequence[Tuple[Any, ...]], ...] = () - - _unnamed: bool - _traverse_internals: _TraverseInternalsType = [ - ("_column_args", InternalTraversal.dp_clauseelement_list), - ("_data", InternalTraversal.dp_dml_multi_values), - ("name", InternalTraversal.dp_string), - ("literal_binds", InternalTraversal.dp_boolean), - ] - - def __init__( - self, - *columns: ColumnClause[Any], - name: Optional[str] = None, - literal_binds: bool = False, - ): - super().__init__() - self._column_args = columns - - if name is None: - self._unnamed = True - self.name = _anonymous_label.safe_construct(id(self), "anon") - else: - self._unnamed = False - self.name = name - self.literal_binds = literal_binds - self.named_with_column = not self._unnamed - - @property - def _column_types(self) -> List[TypeEngine[Any]]: - return [col.type for col in self._column_args] - - @_generative - def alias(self, name: Optional[str] = None, flat: bool = False) -> Self: - """Return a new :class:`_expression.Values` - construct that is a copy of this - one with the given name. - - This method is a VALUES-specific specialization of the - :meth:`_expression.FromClause.alias` method. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - non_none_name: str - - if name is None: - non_none_name = _anonymous_label.safe_construct(id(self), "anon") - else: - non_none_name = name - - self.name = non_none_name - self.named_with_column = True - self._unnamed = False - return self - - @_generative - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a new :class:`_expression.Values` with the lateral flag set, - so that - it renders as LATERAL. - - .. seealso:: - - :func:`_expression.lateral` - - """ - non_none_name: str - - if name is None: - non_none_name = self.name - else: - non_none_name = name - - self._is_lateral = True - self.name = non_none_name - self._unnamed = False - return self - - @_generative - def data(self, values: Sequence[Tuple[Any, ...]]) -> Self: - """Return a new :class:`_expression.Values` construct, - adding the given data to the data list. - - E.g.:: - - my_values = my_values.data([(1, 'value 1'), (2, 'value2')]) - - :param values: a sequence (i.e. list) of tuples that map to the - column expressions given in the :class:`_expression.Values` - constructor. - - """ - - self._data += (values,) - return self - - def scalar_values(self) -> ScalarValues: - """Returns a scalar ``VALUES`` construct that can be used as a - COLUMN element in a statement. - - .. versionadded:: 2.0.0b4 - - """ - return ScalarValues(self._column_args, self._data, self.literal_binds) - - def _populate_column_collection(self) -> None: - for c in self._column_args: - if c.table is not None and c.table is not self: - _, c = c._make_proxy(self) - else: - # if the column was used in other contexts, ensure - # no memoizations of other FROM clauses. - # see test_values.py -> test_auto_proxy_select_direct_col - c._reset_memoizations() - self._columns.add(c) - c.table = self - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [self] - - -class ScalarValues(roles.InElementRole, GroupedElement, ColumnElement[Any]): - """Represent a scalar ``VALUES`` construct that can be used as a - COLUMN element in a statement. - - The :class:`_expression.ScalarValues` object is created from the - :meth:`_expression.Values.scalar_values` method. It's also - automatically generated when a :class:`_expression.Values` is used in - an ``IN`` or ``NOT IN`` condition. - - .. versionadded:: 2.0.0b4 - - """ - - __visit_name__ = "scalar_values" - - _traverse_internals: _TraverseInternalsType = [ - ("_column_args", InternalTraversal.dp_clauseelement_list), - ("_data", InternalTraversal.dp_dml_multi_values), - ("literal_binds", InternalTraversal.dp_boolean), - ] - - def __init__( - self, - columns: Sequence[ColumnClause[Any]], - data: Tuple[Sequence[Tuple[Any, ...]], ...], - literal_binds: bool, - ): - super().__init__() - self._column_args = columns - self._data = data - self.literal_binds = literal_binds - - @property - def _column_types(self) -> List[TypeEngine[Any]]: - return [col.type for col in self._column_args] - - def __clause_element__(self) -> ScalarValues: - return self - - -class SelectBase( - roles.SelectStatementRole, - roles.DMLSelectRole, - roles.CompoundElementRole, - roles.InElementRole, - HasCTE, - SupportsCloneAnnotations, - Selectable, -): - """Base class for SELECT statements. - - - This includes :class:`_expression.Select`, - :class:`_expression.CompoundSelect` and - :class:`_expression.TextualSelect`. - - - """ - - _is_select_base = True - is_select = True - - _label_style: SelectLabelStyle = LABEL_STYLE_NONE - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - self._reset_memoizations() - - @util.ro_non_memoized_property - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - .. note:: - - The :attr:`_sql.SelectBase.selected_columns` collection does not - include expressions established in the columns clause using the - :func:`_sql.text` construct; these are silently omitted from the - collection. To use plain textual column expressions inside of a - :class:`_sql.Select` construct, use the :func:`_sql.literal_column` - construct. - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - .. versionadded:: 1.4 - - """ - raise NotImplementedError() - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - raise NotImplementedError() - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - """A sequence of expressions that correspond to what is rendered - in the columns clause, including :class:`_sql.TextClause` - constructs. - - .. versionadded:: 1.4.12 - - .. seealso:: - - :attr:`_sql.SelectBase.exported_columns` - - """ - raise NotImplementedError() - - @property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.Selectable`, not including - :class:`_sql.TextClause` constructs. - - The "exported" columns for a :class:`_expression.SelectBase` - object are synonymous - with the :attr:`_expression.SelectBase.selected_columns` collection. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.Select.exported_columns` - - :attr:`_expression.Selectable.exported_columns` - - :attr:`_expression.FromClause.exported_columns` - - - """ - return self.selected_columns.as_readonly() - - @property - @util.deprecated( - "1.4", - "The :attr:`_expression.SelectBase.c` and " - ":attr:`_expression.SelectBase.columns` attributes " - "are deprecated and will be removed in a future release; these " - "attributes implicitly create a subquery that should be explicit. " - "Please call :meth:`_expression.SelectBase.subquery` " - "first in order to create " - "a subquery, which then contains this attribute. To access the " - "columns that this SELECT object SELECTs " - "from, use the :attr:`_expression.SelectBase.selected_columns` " - "attribute.", - ) - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self._implicit_subquery.columns - - @property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.c - - def get_label_style(self) -> SelectLabelStyle: - """ - Retrieve the current label style. - - Implemented by subclasses. - - """ - raise NotImplementedError() - - def set_label_style(self, style: SelectLabelStyle) -> Self: - """Return a new selectable with the specified label style. - - Implemented by subclasses. - - """ - - raise NotImplementedError() - - @util.deprecated( - "1.4", - "The :meth:`_expression.SelectBase.select` method is deprecated " - "and will be removed in a future release; this method implicitly " - "creates a subquery that should be explicit. " - "Please call :meth:`_expression.SelectBase.subquery` " - "first in order to create " - "a subquery, which then can be selected.", - ) - def select(self, *arg: Any, **kw: Any) -> Select[Any]: - return self._implicit_subquery.select(*arg, **kw) - - @HasMemoized.memoized_attribute - def _implicit_subquery(self) -> Subquery: - return self.subquery() - - def _scalar_type(self) -> TypeEngine[Any]: - raise NotImplementedError() - - @util.deprecated( - "1.4", - "The :meth:`_expression.SelectBase.as_scalar` " - "method is deprecated and will be " - "removed in a future release. Please refer to " - ":meth:`_expression.SelectBase.scalar_subquery`.", - ) - def as_scalar(self) -> ScalarSelect[Any]: - return self.scalar_subquery() - - def exists(self) -> Exists: - """Return an :class:`_sql.Exists` representation of this selectable, - which can be used as a column expression. - - The returned object is an instance of :class:`_sql.Exists`. - - .. seealso:: - - :func:`_sql.exists` - - :ref:`tutorial_exists` - in the :term:`2.0 style` tutorial. - - .. versionadded:: 1.4 - - """ - return Exists(self) - - def scalar_subquery(self) -> ScalarSelect[Any]: - """Return a 'scalar' representation of this selectable, which can be - used as a column expression. - - The returned object is an instance of :class:`_sql.ScalarSelect`. - - Typically, a select statement which has only one column in its columns - clause is eligible to be used as a scalar expression. The scalar - subquery can then be used in the WHERE clause or columns clause of - an enclosing SELECT. - - Note that the scalar subquery differentiates from the FROM-level - subquery that can be produced using the - :meth:`_expression.SelectBase.subquery` - method. - - .. versionchanged: 1.4 - the ``.as_scalar()`` method was renamed to - :meth:`_expression.SelectBase.scalar_subquery`. - - .. seealso:: - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - """ - if self._label_style is not LABEL_STYLE_NONE: - self = self.set_label_style(LABEL_STYLE_NONE) - - return ScalarSelect(self) - - def label(self, name: Optional[str]) -> Label[Any]: - """Return a 'scalar' representation of this selectable, embedded as a - subquery with a label. - - .. seealso:: - - :meth:`_expression.SelectBase.scalar_subquery`. - - """ - return self.scalar_subquery().label(name) - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a LATERAL alias of this :class:`_expression.Selectable`. - - The return value is the :class:`_expression.Lateral` construct also - provided by the top-level :func:`_expression.lateral` function. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - return Lateral._factory(self, name) - - def subquery(self, name: Optional[str] = None) -> Subquery: - """Return a subquery of this :class:`_expression.SelectBase`. - - A subquery is from a SQL perspective a parenthesized, named - construct that can be placed in the FROM clause of another - SELECT statement. - - Given a SELECT statement such as:: - - stmt = select(table.c.id, table.c.name) - - The above statement might look like:: - - SELECT table.id, table.name FROM table - - The subquery form by itself renders the same way, however when - embedded into the FROM clause of another SELECT statement, it becomes - a named sub-element:: - - subq = stmt.subquery() - new_stmt = select(subq) - - The above renders as:: - - SELECT anon_1.id, anon_1.name - FROM (SELECT table.id, table.name FROM table) AS anon_1 - - Historically, :meth:`_expression.SelectBase.subquery` - is equivalent to calling - the :meth:`_expression.FromClause.alias` - method on a FROM object; however, - as a :class:`_expression.SelectBase` - object is not directly FROM object, - the :meth:`_expression.SelectBase.subquery` - method provides clearer semantics. - - .. versionadded:: 1.4 - - """ - - return Subquery._construct( - self._ensure_disambiguated_names(), name=name - ) - - def _ensure_disambiguated_names(self) -> Self: - """Ensure that the names generated by this selectbase will be - disambiguated in some way, if possible. - - """ - - raise NotImplementedError() - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> Subquery: - """Return a named subquery against this - :class:`_expression.SelectBase`. - - For a :class:`_expression.SelectBase` (as opposed to a - :class:`_expression.FromClause`), - this returns a :class:`.Subquery` object which behaves mostly the - same as the :class:`_expression.Alias` object that is used with a - :class:`_expression.FromClause`. - - .. versionchanged:: 1.4 The :meth:`_expression.SelectBase.alias` - method is now - a synonym for the :meth:`_expression.SelectBase.subquery` method. - - """ - return self.subquery(name=name) - - -_SB = TypeVar("_SB", bound=SelectBase) - - -class SelectStatementGrouping(GroupedElement, SelectBase, Generic[_SB]): - """Represent a grouping of a :class:`_expression.SelectBase`. - - This differs from :class:`.Subquery` in that we are still - an "inner" SELECT statement, this is strictly for grouping inside of - compound selects. - - """ - - __visit_name__ = "select_statement_grouping" - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement) - ] - - _is_select_container = True - - element: _SB - - def __init__(self, element: _SB) -> None: - self.element = cast( - _SB, coercions.expect(roles.SelectStatementRole, element) - ) - - def _ensure_disambiguated_names(self) -> SelectStatementGrouping[_SB]: - new_element = self.element._ensure_disambiguated_names() - if new_element is not self.element: - return SelectStatementGrouping(new_element) - else: - return self - - def get_label_style(self) -> SelectLabelStyle: - return self.element.get_label_style() - - def set_label_style( - self, label_style: SelectLabelStyle - ) -> SelectStatementGrouping[_SB]: - return SelectStatementGrouping( - self.element.set_label_style(label_style) - ) - - @property - def select_statement(self) -> _SB: - return self.element - - def self_group(self, against: Optional[OperatorType] = None) -> Self: - ... - return self - - if TYPE_CHECKING: - - def _ungroup(self) -> _SB: ... - - # def _generate_columns_plus_names( - # self, anon_for_dupe_key: bool - # ) -> List[Tuple[str, str, str, ColumnElement[Any], bool]]: - # return self.element._generate_columns_plus_names(anon_for_dupe_key) - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - self.element._generate_fromclause_column_proxies( - subquery, proxy_compound_columns=proxy_compound_columns - ) - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.element._all_selected_columns - - @util.ro_non_memoized_property - def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - the embedded SELECT statement returns in its result set, not including - :class:`_sql.TextClause` constructs. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - """ - return self.element.selected_columns - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - -class GenerativeSelect(SelectBase, Generative): - """Base class for SELECT statements where additional elements can be - added. - - This serves as the base for :class:`_expression.Select` and - :class:`_expression.CompoundSelect` - where elements such as ORDER BY, GROUP BY can be added and column - rendering can be controlled. Compare to - :class:`_expression.TextualSelect`, which, - while it subclasses :class:`_expression.SelectBase` - and is also a SELECT construct, - represents a fixed textual string which cannot be altered at this level, - only wrapped as a subquery. - - """ - - _order_by_clauses: Tuple[ColumnElement[Any], ...] = () - _group_by_clauses: Tuple[ColumnElement[Any], ...] = () - _limit_clause: Optional[ColumnElement[Any]] = None - _offset_clause: Optional[ColumnElement[Any]] = None - _fetch_clause: Optional[ColumnElement[Any]] = None - _fetch_clause_options: Optional[Dict[str, bool]] = None - _for_update_arg: Optional[ForUpdateArg] = None - - def __init__(self, _label_style: SelectLabelStyle = LABEL_STYLE_DEFAULT): - self._label_style = _label_style - - @_generative - def with_for_update( - self, - *, - nowait: bool = False, - read: bool = False, - of: Optional[_ForUpdateOfArgument] = None, - skip_locked: bool = False, - key_share: bool = False, - ) -> Self: - """Specify a ``FOR UPDATE`` clause for this - :class:`_expression.GenerativeSelect`. - - E.g.:: - - stmt = select(table).with_for_update(nowait=True) - - On a database like PostgreSQL or Oracle, the above would render a - statement like:: - - SELECT table.a, table.b FROM table FOR UPDATE NOWAIT - - on other backends, the ``nowait`` option is ignored and instead - would produce:: - - SELECT table.a, table.b FROM table FOR UPDATE - - When called with no arguments, the statement will render with - the suffix ``FOR UPDATE``. Additional arguments can then be - provided which allow for common database-specific - variants. - - :param nowait: boolean; will render ``FOR UPDATE NOWAIT`` on Oracle - and PostgreSQL dialects. - - :param read: boolean; will render ``LOCK IN SHARE MODE`` on MySQL, - ``FOR SHARE`` on PostgreSQL. On PostgreSQL, when combined with - ``nowait``, will render ``FOR SHARE NOWAIT``. - - :param of: SQL expression or list of SQL expression elements, - (typically :class:`_schema.Column` objects or a compatible expression, - for some backends may also be a table expression) which will render - into a ``FOR UPDATE OF`` clause; supported by PostgreSQL, Oracle, some - MySQL versions and possibly others. May render as a table or as a - column depending on backend. - - :param skip_locked: boolean, will render ``FOR UPDATE SKIP LOCKED`` - on Oracle and PostgreSQL dialects or ``FOR SHARE SKIP LOCKED`` if - ``read=True`` is also specified. - - :param key_share: boolean, will render ``FOR NO KEY UPDATE``, - or if combined with ``read=True`` will render ``FOR KEY SHARE``, - on the PostgreSQL dialect. - - """ - self._for_update_arg = ForUpdateArg( - nowait=nowait, - read=read, - of=of, - skip_locked=skip_locked, - key_share=key_share, - ) - return self - - def get_label_style(self) -> SelectLabelStyle: - """ - Retrieve the current label style. - - .. versionadded:: 1.4 - - """ - return self._label_style - - def set_label_style(self, style: SelectLabelStyle) -> Self: - """Return a new selectable with the specified label style. - - There are three "label styles" available, - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`, - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`, and - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_NONE`. The default style is - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`. - - In modern SQLAlchemy, there is not generally a need to change the - labeling style, as per-expression labels are more effectively used by - making use of the :meth:`_sql.ColumnElement.label` method. In past - versions, :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` was used to - disambiguate same-named columns from different tables, aliases, or - subqueries; the newer :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` now - applies labels only to names that conflict with an existing name so - that the impact of this labeling is minimal. - - The rationale for disambiguation is mostly so that all column - expressions are available from a given :attr:`_sql.FromClause.c` - collection when a subquery is created. - - .. versionadded:: 1.4 - the - :meth:`_sql.GenerativeSelect.set_label_style` method replaces the - previous combination of ``.apply_labels()``, ``.with_labels()`` and - ``use_labels=True`` methods and/or parameters. - - .. seealso:: - - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` - - :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` - - :data:`_sql.LABEL_STYLE_NONE` - - :data:`_sql.LABEL_STYLE_DEFAULT` - - """ - if self._label_style is not style: - self = self._generate() - self._label_style = style - return self - - @property - def _group_by_clause(self) -> ClauseList: - """ClauseList access to group_by_clauses for legacy dialects""" - return ClauseList._construct_raw( - operators.comma_op, self._group_by_clauses - ) - - @property - def _order_by_clause(self) -> ClauseList: - """ClauseList access to order_by_clauses for legacy dialects""" - return ClauseList._construct_raw( - operators.comma_op, self._order_by_clauses - ) - - def _offset_or_limit_clause( - self, - element: _LimitOffsetType, - name: Optional[str] = None, - type_: Optional[_TypeEngineArgument[int]] = None, - ) -> ColumnElement[Any]: - """Convert the given value to an "offset or limit" clause. - - This handles incoming integers and converts to an expression; if - an expression is already given, it is passed through. - - """ - return coercions.expect( - roles.LimitOffsetRole, element, name=name, type_=type_ - ) - - @overload - def _offset_or_limit_clause_asint( - self, clause: ColumnElement[Any], attrname: str - ) -> NoReturn: ... - - @overload - def _offset_or_limit_clause_asint( - self, clause: Optional[_OffsetLimitParam], attrname: str - ) -> Optional[int]: ... - - def _offset_or_limit_clause_asint( - self, clause: Optional[ColumnElement[Any]], attrname: str - ) -> Union[NoReturn, Optional[int]]: - """Convert the "offset or limit" clause of a select construct to an - integer. - - This is only possible if the value is stored as a simple bound - parameter. Otherwise, a compilation error is raised. - - """ - if clause is None: - return None - try: - value = clause._limit_offset_value - except AttributeError as err: - raise exc.CompileError( - "This SELECT structure does not use a simple " - "integer value for %s" % attrname - ) from err - else: - return util.asint(value) - - @property - def _limit(self) -> Optional[int]: - """Get an integer value for the limit. This should only be used - by code that cannot support a limit as a BindParameter or - other custom clause as it will throw an exception if the limit - isn't currently set to an integer. - - """ - return self._offset_or_limit_clause_asint(self._limit_clause, "limit") - - def _simple_int_clause(self, clause: ClauseElement) -> bool: - """True if the clause is a simple integer, False - if it is not present or is a SQL expression. - """ - return isinstance(clause, _OffsetLimitParam) - - @property - def _offset(self) -> Optional[int]: - """Get an integer value for the offset. This should only be used - by code that cannot support an offset as a BindParameter or - other custom clause as it will throw an exception if the - offset isn't currently set to an integer. - - """ - return self._offset_or_limit_clause_asint( - self._offset_clause, "offset" - ) - - @property - def _has_row_limiting_clause(self) -> bool: - return ( - self._limit_clause is not None - or self._offset_clause is not None - or self._fetch_clause is not None - ) - - @_generative - def limit(self, limit: _LimitOffsetType) -> Self: - """Return a new selectable with the given LIMIT criterion - applied. - - This is a numerical value which usually renders as a ``LIMIT`` - expression in the resulting select. Backends that don't - support ``LIMIT`` will attempt to provide similar - functionality. - - .. note:: - - The :meth:`_sql.GenerativeSelect.limit` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.fetch`. - - :param limit: an integer LIMIT parameter, or a SQL expression - that provides an integer result. Pass ``None`` to reset it. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.fetch` - - :meth:`_sql.GenerativeSelect.offset` - - """ - - self._fetch_clause = self._fetch_clause_options = None - self._limit_clause = self._offset_or_limit_clause(limit) - return self - - @_generative - def fetch( - self, - count: _LimitOffsetType, - with_ties: bool = False, - percent: bool = False, - ) -> Self: - """Return a new selectable with the given FETCH FIRST criterion - applied. - - This is a numeric value which usually renders as - ``FETCH {FIRST | NEXT} [ count ] {ROW | ROWS} {ONLY | WITH TIES}`` - expression in the resulting select. This functionality is - is currently implemented for Oracle, PostgreSQL, MSSQL. - - Use :meth:`_sql.GenerativeSelect.offset` to specify the offset. - - .. note:: - - The :meth:`_sql.GenerativeSelect.fetch` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.limit`. - - .. versionadded:: 1.4 - - :param count: an integer COUNT parameter, or a SQL expression - that provides an integer result. When ``percent=True`` this will - represent the percentage of rows to return, not the absolute value. - Pass ``None`` to reset it. - - :param with_ties: When ``True``, the WITH TIES option is used - to return any additional rows that tie for the last place in the - result set according to the ``ORDER BY`` clause. The - ``ORDER BY`` may be mandatory in this case. Defaults to ``False`` - - :param percent: When ``True``, ``count`` represents the percentage - of the total number of selected rows to return. Defaults to ``False`` - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.offset` - - """ - - self._limit_clause = None - if count is None: - self._fetch_clause = self._fetch_clause_options = None - else: - self._fetch_clause = self._offset_or_limit_clause(count) - self._fetch_clause_options = { - "with_ties": with_ties, - "percent": percent, - } - return self - - @_generative - def offset(self, offset: _LimitOffsetType) -> Self: - """Return a new selectable with the given OFFSET criterion - applied. - - - This is a numeric value which usually renders as an ``OFFSET`` - expression in the resulting select. Backends that don't - support ``OFFSET`` will attempt to provide similar - functionality. - - :param offset: an integer OFFSET parameter, or a SQL expression - that provides an integer result. Pass ``None`` to reset it. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.fetch` - - """ - - self._offset_clause = self._offset_or_limit_clause(offset) - return self - - @_generative - @util.preload_module("sqlalchemy.sql.util") - def slice( - self, - start: int, - stop: int, - ) -> Self: - """Apply LIMIT / OFFSET to this statement based on a slice. - - The start and stop indices behave like the argument to Python's - built-in :func:`range` function. This method provides an - alternative to using ``LIMIT``/``OFFSET`` to get a slice of the - query. - - For example, :: - - stmt = select(User).order_by(User).id.slice(1, 3) - - renders as - - .. sourcecode:: sql - - SELECT users.id AS users_id, - users.name AS users_name - FROM users ORDER BY users.id - LIMIT ? OFFSET ? - (2, 1) - - .. note:: - - The :meth:`_sql.GenerativeSelect.slice` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.fetch`. - - .. versionadded:: 1.4 Added the :meth:`_sql.GenerativeSelect.slice` - method generalized from the ORM. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.offset` - - :meth:`_sql.GenerativeSelect.fetch` - - """ - sql_util = util.preloaded.sql_util - self._fetch_clause = self._fetch_clause_options = None - self._limit_clause, self._offset_clause = sql_util._make_slice( - self._limit_clause, self._offset_clause, start, stop - ) - return self - - @_generative - def order_by( - self, - __first: Union[ - Literal[None, _NoArg.NO_ARG], - _ColumnExpressionOrStrLabelArgument[Any], - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionOrStrLabelArgument[Any], - ) -> Self: - r"""Return a new selectable with the given list of ORDER BY - criteria applied. - - e.g.:: - - stmt = select(table).order_by(table.c.id, table.c.name) - - Calling this method multiple times is equivalent to calling it once - with all the clauses concatenated. All existing ORDER BY criteria may - be cancelled by passing ``None`` by itself. New ORDER BY criteria may - then be added by invoking :meth:`_orm.Query.order_by` again, e.g.:: - - # will erase all ORDER BY and ORDER BY new_col alone - stmt = stmt.order_by(None).order_by(new_col) - - :param \*clauses: a series of :class:`_expression.ColumnElement` - constructs - which will be used to generate an ORDER BY clause. - - .. seealso:: - - :ref:`tutorial_order_by` - in the :ref:`unified_tutorial` - - :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial` - - """ - - if not clauses and __first is None: - self._order_by_clauses = () - elif __first is not _NoArg.NO_ARG: - self._order_by_clauses += tuple( - coercions.expect( - roles.OrderByRole, clause, apply_propagate_attrs=self - ) - for clause in (__first,) + clauses - ) - return self - - @_generative - def group_by( - self, - __first: Union[ - Literal[None, _NoArg.NO_ARG], - _ColumnExpressionOrStrLabelArgument[Any], - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionOrStrLabelArgument[Any], - ) -> Self: - r"""Return a new selectable with the given list of GROUP BY - criterion applied. - - All existing GROUP BY settings can be suppressed by passing ``None``. - - e.g.:: - - stmt = select(table.c.name, func.max(table.c.stat)).\ - group_by(table.c.name) - - :param \*clauses: a series of :class:`_expression.ColumnElement` - constructs - which will be used to generate an GROUP BY clause. - - .. seealso:: - - :ref:`tutorial_group_by_w_aggregates` - in the - :ref:`unified_tutorial` - - :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial` - - """ - - if not clauses and __first is None: - self._group_by_clauses = () - elif __first is not _NoArg.NO_ARG: - self._group_by_clauses += tuple( - coercions.expect( - roles.GroupByRole, clause, apply_propagate_attrs=self - ) - for clause in (__first,) + clauses - ) - return self - - -@CompileState.plugin_for("default", "compound_select") -class CompoundSelectState(CompileState): - @util.memoized_property - def _label_resolve_dict( - self, - ) -> Tuple[ - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - ]: - # TODO: this is hacky and slow - hacky_subquery = self.statement.subquery() - hacky_subquery.named_with_column = False - d = {c.key: c for c in hacky_subquery.c} - return d, d, d - - -class _CompoundSelectKeyword(Enum): - UNION = "UNION" - UNION_ALL = "UNION ALL" - EXCEPT = "EXCEPT" - EXCEPT_ALL = "EXCEPT ALL" - INTERSECT = "INTERSECT" - INTERSECT_ALL = "INTERSECT ALL" - - -class CompoundSelect(HasCompileState, GenerativeSelect, ExecutableReturnsRows): - """Forms the basis of ``UNION``, ``UNION ALL``, and other - SELECT-based set operations. - - - .. seealso:: - - :func:`_expression.union` - - :func:`_expression.union_all` - - :func:`_expression.intersect` - - :func:`_expression.intersect_all` - - :func:`_expression.except` - - :func:`_expression.except_all` - - """ - - __visit_name__ = "compound_select" - - _traverse_internals: _TraverseInternalsType = [ - ("selects", InternalTraversal.dp_clauseelement_list), - ("_limit_clause", InternalTraversal.dp_clauseelement), - ("_offset_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause_options", InternalTraversal.dp_plain_dict), - ("_order_by_clauses", InternalTraversal.dp_clauseelement_list), - ("_group_by_clauses", InternalTraversal.dp_clauseelement_list), - ("_for_update_arg", InternalTraversal.dp_clauseelement), - ("keyword", InternalTraversal.dp_string), - ] + SupportsCloneAnnotations._clone_annotations_traverse_internals - - selects: List[SelectBase] - - _is_from_container = True - _auto_correlate = False - - def __init__( - self, - keyword: _CompoundSelectKeyword, - *selects: _SelectStatementForCompoundArgument, - ): - self.keyword = keyword - self.selects = [ - coercions.expect( - roles.CompoundElementRole, s, apply_propagate_attrs=self - ).self_group(against=self) - for s in selects - ] - - GenerativeSelect.__init__(self) - - @classmethod - def _create_union( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.UNION, *selects) - - @classmethod - def _create_union_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.UNION_ALL, *selects) - - @classmethod - def _create_except( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.EXCEPT, *selects) - - @classmethod - def _create_except_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.EXCEPT_ALL, *selects) - - @classmethod - def _create_intersect( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.INTERSECT, *selects) - - @classmethod - def _create_intersect_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.INTERSECT_ALL, *selects) - - def _scalar_type(self) -> TypeEngine[Any]: - return self.selects[0]._scalar_type() - - def self_group( - self, against: Optional[OperatorType] = None - ) -> GroupedElement: - return SelectStatementGrouping(self) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - for s in self.selects: - if s.is_derived_from(fromclause): - return True - return False - - def set_label_style(self, style: SelectLabelStyle) -> CompoundSelect: - if self._label_style is not style: - self = self._generate() - select_0 = self.selects[0].set_label_style(style) - self.selects = [select_0] + self.selects[1:] - - return self - - def _ensure_disambiguated_names(self) -> CompoundSelect: - new_select = self.selects[0]._ensure_disambiguated_names() - if new_select is not self.selects[0]: - self = self._generate() - self.selects = [new_select] + self.selects[1:] - - return self - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - # this is a slightly hacky thing - the union exports a - # column that resembles just that of the *first* selectable. - # to get at a "composite" column, particularly foreign keys, - # you have to dig through the proxies collection which we - # generate below. - select_0 = self.selects[0] - - if self._label_style is not LABEL_STYLE_DEFAULT: - select_0 = select_0.set_label_style(self._label_style) - - # hand-construct the "_proxies" collection to include all - # derived columns place a 'weight' annotation corresponding - # to how low in the list of select()s the column occurs, so - # that the corresponding_column() operation can resolve - # conflicts - extra_col_iterator = zip( - *[ - [ - c._annotate(dd) - for c in stmt._all_selected_columns - if is_column_element(c) - ] - for dd, stmt in [ - ({"weight": i + 1}, stmt) - for i, stmt in enumerate(self.selects) - ] - ] - ) - - # the incoming proxy_compound_columns can be present also if this is - # a compound embedded in a compound. it's probably more appropriate - # that we generate new weights local to this nested compound, though - # i haven't tried to think what it means for compound nested in - # compound - select_0._generate_fromclause_column_proxies( - subquery, proxy_compound_columns=extra_col_iterator - ) - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - for select in self.selects: - select._refresh_for_new_column(column) - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.selects[0]._all_selected_columns - - @util.ro_non_memoized_property - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - For a :class:`_expression.CompoundSelect`, the - :attr:`_expression.CompoundSelect.selected_columns` - attribute returns the selected - columns of the first SELECT statement contained within the series of - statements within the set operation. - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - .. versionadded:: 1.4 - - """ - return self.selects[0].selected_columns - - -# backwards compat -for elem in _CompoundSelectKeyword: - setattr(CompoundSelect, elem.name, elem) - - -@CompileState.plugin_for("default", "select") -class SelectState(util.MemoizedSlots, CompileState): - __slots__ = ( - "from_clauses", - "froms", - "columns_plus_names", - "_label_resolve_dict", - ) - - if TYPE_CHECKING: - default_select_compile_options: CacheableOptions - else: - - class default_select_compile_options(CacheableOptions): - _cache_key_traversal = [] - - if TYPE_CHECKING: - - @classmethod - def get_plugin_class( - cls, statement: Executable - ) -> Type[SelectState]: ... - - def __init__( - self, - statement: Select[Any], - compiler: Optional[SQLCompiler], - **kw: Any, - ): - self.statement = statement - self.from_clauses = statement._from_obj - - for memoized_entities in statement._memoized_select_entities: - self._setup_joins( - memoized_entities._setup_joins, memoized_entities._raw_columns - ) - - if statement._setup_joins: - self._setup_joins(statement._setup_joins, statement._raw_columns) - - self.froms = self._get_froms(statement) - - self.columns_plus_names = statement._generate_columns_plus_names(True) - - @classmethod - def _plugin_not_implemented(cls) -> NoReturn: - raise NotImplementedError( - "The default SELECT construct without plugins does not " - "implement this method." - ) - - @classmethod - def get_column_descriptions( - cls, statement: Select[Any] - ) -> List[Dict[str, Any]]: - return [ - { - "name": name, - "type": element.type, - "expr": element, - } - for _, name, _, element, _ in ( - statement._generate_columns_plus_names(False) - ) - ] - - @classmethod - def from_statement( - cls, statement: Select[Any], from_statement: roles.ReturnsRowsRole - ) -> ExecutableReturnsRows: - cls._plugin_not_implemented() - - @classmethod - def get_columns_clause_froms( - cls, statement: Select[Any] - ) -> List[FromClause]: - return cls._normalize_froms( - itertools.chain.from_iterable( - element._from_objects for element in statement._raw_columns - ) - ) - - @classmethod - def _column_naming_convention( - cls, label_style: SelectLabelStyle - ) -> _LabelConventionCallable: - table_qualified = label_style is LABEL_STYLE_TABLENAME_PLUS_COL - dedupe = label_style is not LABEL_STYLE_NONE - - pa = prefix_anon_map() - names = set() - - def go( - c: Union[ColumnElement[Any], TextClause], - col_name: Optional[str] = None, - ) -> Optional[str]: - if is_text_clause(c): - return None - elif TYPE_CHECKING: - assert is_column_element(c) - - if not dedupe: - name = c._proxy_key - if name is None: - name = "_no_label" - return name - - name = c._tq_key_label if table_qualified else c._proxy_key - - if name is None: - name = "_no_label" - if name in names: - return c._anon_label(name) % pa - else: - names.add(name) - return name - - elif name in names: - return ( - c._anon_tq_key_label % pa - if table_qualified - else c._anon_key_label % pa - ) - else: - names.add(name) - return name - - return go - - def _get_froms(self, statement: Select[Any]) -> List[FromClause]: - ambiguous_table_name_map: _AmbiguousTableNameMap - self._ambiguous_table_name_map = ambiguous_table_name_map = {} - - return self._normalize_froms( - itertools.chain( - self.from_clauses, - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._raw_columns - ] - ), - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._where_criteria - ] - ), - ), - check_statement=statement, - ambiguous_table_name_map=ambiguous_table_name_map, - ) - - @classmethod - def _normalize_froms( - cls, - iterable_of_froms: Iterable[FromClause], - check_statement: Optional[Select[Any]] = None, - ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None, - ) -> List[FromClause]: - """given an iterable of things to select FROM, reduce them to what - would actually render in the FROM clause of a SELECT. - - This does the job of checking for JOINs, tables, etc. that are in fact - overlapping due to cloning, adaption, present in overlapping joins, - etc. - - """ - seen: Set[FromClause] = set() - froms: List[FromClause] = [] - - for item in iterable_of_froms: - if is_subquery(item) and item.element is check_statement: - raise exc.InvalidRequestError( - "select() construct refers to itself as a FROM" - ) - - if not seen.intersection(item._cloned_set): - froms.append(item) - seen.update(item._cloned_set) - - if froms: - toremove = set( - itertools.chain.from_iterable( - [_expand_cloned(f._hide_froms) for f in froms] - ) - ) - if toremove: - # filter out to FROM clauses not in the list, - # using a list to maintain ordering - froms = [f for f in froms if f not in toremove] - - if ambiguous_table_name_map is not None: - ambiguous_table_name_map.update( - ( - fr.name, - _anonymous_label.safe_construct( - hash(fr.name), fr.name - ), - ) - for item in froms - for fr in item._from_objects - if is_table(fr) - and fr.schema - and fr.name not in ambiguous_table_name_map - ) - - return froms - - def _get_display_froms( - self, - explicit_correlate_froms: Optional[Sequence[FromClause]] = None, - implicit_correlate_froms: Optional[Sequence[FromClause]] = None, - ) -> List[FromClause]: - """Return the full list of 'from' clauses to be displayed. - - Takes into account a set of existing froms which may be - rendered in the FROM clause of enclosing selects; this Select - may want to leave those absent if it is automatically - correlating. - - """ - - froms = self.froms - - if self.statement._correlate: - to_correlate = self.statement._correlate - if to_correlate: - froms = [ - f - for f in froms - if f - not in _cloned_intersection( - _cloned_intersection( - froms, explicit_correlate_froms or () - ), - to_correlate, - ) - ] - - if self.statement._correlate_except is not None: - froms = [ - f - for f in froms - if f - not in _cloned_difference( - _cloned_intersection( - froms, explicit_correlate_froms or () - ), - self.statement._correlate_except, - ) - ] - - if ( - self.statement._auto_correlate - and implicit_correlate_froms - and len(froms) > 1 - ): - froms = [ - f - for f in froms - if f - not in _cloned_intersection(froms, implicit_correlate_froms) - ] - - if not len(froms): - raise exc.InvalidRequestError( - "Select statement '%r" - "' returned no FROM clauses " - "due to auto-correlation; " - "specify correlate(<tables>) " - "to control correlation " - "manually." % self.statement - ) - - return froms - - def _memoized_attr__label_resolve_dict( - self, - ) -> Tuple[ - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - ]: - with_cols: Dict[str, ColumnElement[Any]] = { - c._tq_label or c.key: c - for c in self.statement._all_selected_columns - if c._allow_label_resolve - } - only_froms: Dict[str, ColumnElement[Any]] = { - c.key: c # type: ignore - for c in _select_iterables(self.froms) - if c._allow_label_resolve - } - only_cols: Dict[str, ColumnElement[Any]] = with_cols.copy() - for key, value in only_froms.items(): - with_cols.setdefault(key, value) - - return with_cols, only_froms, only_cols - - @classmethod - def determine_last_joined_entity( - cls, stmt: Select[Any] - ) -> Optional[_JoinTargetElement]: - if stmt._setup_joins: - return stmt._setup_joins[-1][0] - else: - return None - - @classmethod - def all_selected_columns(cls, statement: Select[Any]) -> _SelectIterable: - return [c for c in _select_iterables(statement._raw_columns)] - - def _setup_joins( - self, - args: Tuple[_SetupJoinsElement, ...], - raw_columns: List[_ColumnsClauseElement], - ) -> None: - for right, onclause, left, flags in args: - if TYPE_CHECKING: - if onclause is not None: - assert isinstance(onclause, ColumnElement) - - isouter = flags["isouter"] - full = flags["full"] - - if left is None: - ( - left, - replace_from_obj_index, - ) = self._join_determine_implicit_left_side( - raw_columns, left, right, onclause - ) - else: - (replace_from_obj_index) = self._join_place_explicit_left_side( - left - ) - - # these assertions can be made here, as if the right/onclause - # contained ORM elements, the select() statement would have been - # upgraded to an ORM select, and this method would not be called; - # orm.context.ORMSelectCompileState._join() would be - # used instead. - if TYPE_CHECKING: - assert isinstance(right, FromClause) - if onclause is not None: - assert isinstance(onclause, ColumnElement) - - if replace_from_obj_index is not None: - # splice into an existing element in the - # self._from_obj list - left_clause = self.from_clauses[replace_from_obj_index] - - self.from_clauses = ( - self.from_clauses[:replace_from_obj_index] - + ( - Join( - left_clause, - right, - onclause, - isouter=isouter, - full=full, - ), - ) - + self.from_clauses[replace_from_obj_index + 1 :] - ) - else: - assert left is not None - self.from_clauses = self.from_clauses + ( - Join(left, right, onclause, isouter=isouter, full=full), - ) - - @util.preload_module("sqlalchemy.sql.util") - def _join_determine_implicit_left_side( - self, - raw_columns: List[_ColumnsClauseElement], - left: Optional[FromClause], - right: _JoinTargetElement, - onclause: Optional[ColumnElement[Any]], - ) -> Tuple[Optional[FromClause], Optional[int]]: - """When join conditions don't express the left side explicitly, - determine if an existing FROM or entity in this query - can serve as the left hand side. - - """ - - sql_util = util.preloaded.sql_util - - replace_from_obj_index: Optional[int] = None - - from_clauses = self.from_clauses - - if from_clauses: - indexes: List[int] = sql_util.find_left_clause_to_join_from( - from_clauses, right, onclause - ) - - if len(indexes) == 1: - replace_from_obj_index = indexes[0] - left = from_clauses[replace_from_obj_index] - else: - potential = {} - statement = self.statement - - for from_clause in itertools.chain( - itertools.chain.from_iterable( - [element._from_objects for element in raw_columns] - ), - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._where_criteria - ] - ), - ): - potential[from_clause] = () - - all_clauses = list(potential.keys()) - indexes = sql_util.find_left_clause_to_join_from( - all_clauses, right, onclause - ) - - if len(indexes) == 1: - left = all_clauses[indexes[0]] - - if len(indexes) > 1: - raise exc.InvalidRequestError( - "Can't determine which FROM clause to join " - "from, there are multiple FROMS which can " - "join to this entity. Please use the .select_from() " - "method to establish an explicit left side, as well as " - "providing an explicit ON clause if not present already to " - "help resolve the ambiguity." - ) - elif not indexes: - raise exc.InvalidRequestError( - "Don't know how to join to %r. " - "Please use the .select_from() " - "method to establish an explicit left side, as well as " - "providing an explicit ON clause if not present already to " - "help resolve the ambiguity." % (right,) - ) - return left, replace_from_obj_index - - @util.preload_module("sqlalchemy.sql.util") - def _join_place_explicit_left_side( - self, left: FromClause - ) -> Optional[int]: - replace_from_obj_index: Optional[int] = None - - sql_util = util.preloaded.sql_util - - from_clauses = list(self.statement._iterate_from_elements()) - - if from_clauses: - indexes: List[int] = sql_util.find_left_clause_that_matches_given( - self.from_clauses, left - ) - else: - indexes = [] - - if len(indexes) > 1: - raise exc.InvalidRequestError( - "Can't identify which entity in which to assign the " - "left side of this join. Please use a more specific " - "ON clause." - ) - - # have an index, means the left side is already present in - # an existing FROM in the self._from_obj tuple - if indexes: - replace_from_obj_index = indexes[0] - - # no index, means we need to add a new element to the - # self._from_obj tuple - - return replace_from_obj_index - - -class _SelectFromElements: - __slots__ = () - - _raw_columns: List[_ColumnsClauseElement] - _where_criteria: Tuple[ColumnElement[Any], ...] - _from_obj: Tuple[FromClause, ...] - - def _iterate_from_elements(self) -> Iterator[FromClause]: - # note this does not include elements - # in _setup_joins - - seen = set() - for element in self._raw_columns: - for fr in element._from_objects: - if fr in seen: - continue - seen.add(fr) - yield fr - for element in self._where_criteria: - for fr in element._from_objects: - if fr in seen: - continue - seen.add(fr) - yield fr - for element in self._from_obj: - if element in seen: - continue - seen.add(element) - yield element - - -class _MemoizedSelectEntities( - cache_key.HasCacheKey, traversals.HasCopyInternals, visitors.Traversible -): - """represents partial state from a Select object, for the case - where Select.columns() has redefined the set of columns/entities the - statement will be SELECTing from. This object represents - the entities from the SELECT before that transformation was applied, - so that transformations that were made in terms of the SELECT at that - time, such as join() as well as options(), can access the correct context. - - In previous SQLAlchemy versions, this wasn't needed because these - constructs calculated everything up front, like when you called join() - or options(), it did everything to figure out how that would translate - into specific SQL constructs that would be ready to send directly to the - SQL compiler when needed. But as of - 1.4, all of that stuff is done in the compilation phase, during the - "compile state" portion of the process, so that the work can all be - cached. So it needs to be able to resolve joins/options2 based on what - the list of entities was when those methods were called. - - - """ - - __visit_name__ = "memoized_select_entities" - - _traverse_internals: _TraverseInternalsType = [ - ("_raw_columns", InternalTraversal.dp_clauseelement_list), - ("_setup_joins", InternalTraversal.dp_setup_join_tuple), - ("_with_options", InternalTraversal.dp_executable_options), - ] - - _is_clone_of: Optional[ClauseElement] - _raw_columns: List[_ColumnsClauseElement] - _setup_joins: Tuple[_SetupJoinsElement, ...] - _with_options: Tuple[ExecutableOption, ...] - - _annotations = util.EMPTY_DICT - - def _clone(self, **kw: Any) -> Self: - c = self.__class__.__new__(self.__class__) - c.__dict__ = {k: v for k, v in self.__dict__.items()} - - c._is_clone_of = self.__dict__.get("_is_clone_of", self) - return c - - @classmethod - def _generate_for_statement(cls, select_stmt: Select[Any]) -> None: - if select_stmt._setup_joins or select_stmt._with_options: - self = _MemoizedSelectEntities() - self._raw_columns = select_stmt._raw_columns - self._setup_joins = select_stmt._setup_joins - self._with_options = select_stmt._with_options - - select_stmt._memoized_select_entities += (self,) - select_stmt._raw_columns = [] - select_stmt._setup_joins = select_stmt._with_options = () - - -class Select( - HasPrefixes, - HasSuffixes, - HasHints, - HasCompileState, - _SelectFromElements, - GenerativeSelect, - TypedReturnsRows[_TP], -): - """Represents a ``SELECT`` statement. - - The :class:`_sql.Select` object is normally constructed using the - :func:`_sql.select` function. See that function for details. - - .. seealso:: - - :func:`_sql.select` - - :ref:`tutorial_selecting_data` - in the 2.0 tutorial - - """ - - __visit_name__ = "select" - - _setup_joins: Tuple[_SetupJoinsElement, ...] = () - _memoized_select_entities: Tuple[TODO_Any, ...] = () - - _raw_columns: List[_ColumnsClauseElement] - - _distinct: bool = False - _distinct_on: Tuple[ColumnElement[Any], ...] = () - _correlate: Tuple[FromClause, ...] = () - _correlate_except: Optional[Tuple[FromClause, ...]] = None - _where_criteria: Tuple[ColumnElement[Any], ...] = () - _having_criteria: Tuple[ColumnElement[Any], ...] = () - _from_obj: Tuple[FromClause, ...] = () - _auto_correlate = True - _is_select_statement = True - _compile_options: CacheableOptions = ( - SelectState.default_select_compile_options - ) - - _traverse_internals: _TraverseInternalsType = ( - [ - ("_raw_columns", InternalTraversal.dp_clauseelement_list), - ( - "_memoized_select_entities", - InternalTraversal.dp_memoized_select_entities, - ), - ("_from_obj", InternalTraversal.dp_clauseelement_list), - ("_where_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_having_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_order_by_clauses", InternalTraversal.dp_clauseelement_tuple), - ("_group_by_clauses", InternalTraversal.dp_clauseelement_tuple), - ("_setup_joins", InternalTraversal.dp_setup_join_tuple), - ("_correlate", InternalTraversal.dp_clauseelement_tuple), - ("_correlate_except", InternalTraversal.dp_clauseelement_tuple), - ("_limit_clause", InternalTraversal.dp_clauseelement), - ("_offset_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause_options", InternalTraversal.dp_plain_dict), - ("_for_update_arg", InternalTraversal.dp_clauseelement), - ("_distinct", InternalTraversal.dp_boolean), - ("_distinct_on", InternalTraversal.dp_clauseelement_tuple), - ("_label_style", InternalTraversal.dp_plain_obj), - ] - + HasCTE._has_ctes_traverse_internals - + HasPrefixes._has_prefixes_traverse_internals - + HasSuffixes._has_suffixes_traverse_internals - + HasHints._has_hints_traverse_internals - + SupportsCloneAnnotations._clone_annotations_traverse_internals - + Executable._executable_traverse_internals - ) - - _cache_key_traversal: _CacheKeyTraversalType = _traverse_internals + [ - ("_compile_options", InternalTraversal.dp_has_cache_key) - ] - - _compile_state_factory: Type[SelectState] - - @classmethod - def _create_raw_select(cls, **kw: Any) -> Select[Any]: - """Create a :class:`.Select` using raw ``__new__`` with no coercions. - - Used internally to build up :class:`.Select` constructs with - pre-established state. - - """ - - stmt = Select.__new__(Select) - stmt.__dict__.update(kw) - return stmt - - def __init__(self, *entities: _ColumnsClauseArgument[Any]): - r"""Construct a new :class:`_expression.Select`. - - The public constructor for :class:`_expression.Select` is the - :func:`_sql.select` function. - - """ - self._raw_columns = [ - coercions.expect( - roles.ColumnsClauseRole, ent, apply_propagate_attrs=self - ) - for ent in entities - ] - - GenerativeSelect.__init__(self) - - def _scalar_type(self) -> TypeEngine[Any]: - if not self._raw_columns: - return NULLTYPE - elem = self._raw_columns[0] - cols = list(elem._select_iterable) - return cols[0].type - - def filter(self, *criteria: _ColumnExpressionArgument[bool]) -> Self: - """A synonym for the :meth:`_sql.Select.where` method.""" - - return self.where(*criteria) - - def _filter_by_zero( - self, - ) -> Union[ - FromClause, _JoinTargetProtocol, ColumnElement[Any], TextClause - ]: - if self._setup_joins: - meth = SelectState.get_plugin_class( - self - ).determine_last_joined_entity - _last_joined_entity = meth(self) - if _last_joined_entity is not None: - return _last_joined_entity - - if self._from_obj: - return self._from_obj[0] - - return self._raw_columns[0] - - if TYPE_CHECKING: - - @overload - def scalar_subquery( - self: Select[Tuple[_MAYBE_ENTITY]], - ) -> ScalarSelect[Any]: ... - - @overload - def scalar_subquery( - self: Select[Tuple[_NOT_ENTITY]], - ) -> ScalarSelect[_NOT_ENTITY]: ... - - @overload - def scalar_subquery(self) -> ScalarSelect[Any]: ... - - def scalar_subquery(self) -> ScalarSelect[Any]: ... - - def filter_by(self, **kwargs: Any) -> Self: - r"""apply the given filtering criterion as a WHERE clause - to this select. - - """ - from_entity = self._filter_by_zero() - - clauses = [ - _entity_namespace_key(from_entity, key) == value - for key, value in kwargs.items() - ] - return self.filter(*clauses) - - @property - def column_descriptions(self) -> Any: - """Return a :term:`plugin-enabled` 'column descriptions' structure - referring to the columns which are SELECTed by this statement. - - This attribute is generally useful when using the ORM, as an - extended structure which includes information about mapped - entities is returned. The section :ref:`queryguide_inspection` - contains more background. - - For a Core-only statement, the structure returned by this accessor - is derived from the same objects that are returned by the - :attr:`.Select.selected_columns` accessor, formatted as a list of - dictionaries which contain the keys ``name``, ``type`` and ``expr``, - which indicate the column expressions to be selected:: - - >>> stmt = select(user_table) - >>> stmt.column_descriptions - [ - { - 'name': 'id', - 'type': Integer(), - 'expr': Column('id', Integer(), ...)}, - { - 'name': 'name', - 'type': String(length=30), - 'expr': Column('name', String(length=30), ...)} - ] - - .. versionchanged:: 1.4.33 The :attr:`.Select.column_descriptions` - attribute returns a structure for a Core-only set of entities, - not just ORM-only entities. - - .. seealso:: - - :attr:`.UpdateBase.entity_description` - entity information for - an :func:`.insert`, :func:`.update`, or :func:`.delete` - - :ref:`queryguide_inspection` - ORM background - - """ - meth = SelectState.get_plugin_class(self).get_column_descriptions - return meth(self) - - def from_statement( - self, statement: roles.ReturnsRowsRole - ) -> ExecutableReturnsRows: - """Apply the columns which this :class:`.Select` would select - onto another statement. - - This operation is :term:`plugin-specific` and will raise a not - supported exception if this :class:`_sql.Select` does not select from - plugin-enabled entities. - - - The statement is typically either a :func:`_expression.text` or - :func:`_expression.select` construct, and should return the set of - columns appropriate to the entities represented by this - :class:`.Select`. - - .. seealso:: - - :ref:`orm_queryguide_selecting_text` - usage examples in the - ORM Querying Guide - - """ - meth = SelectState.get_plugin_class(self).from_statement - return meth(self, statement) - - @_generative - def join( - self, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - isouter: bool = False, - full: bool = False, - ) -> Self: - r"""Create a SQL JOIN against this :class:`_expression.Select` - object's criterion - and apply generatively, returning the newly resulting - :class:`_expression.Select`. - - E.g.:: - - stmt = select(user_table).join(address_table, user_table.c.id == address_table.c.user_id) - - The above statement generates SQL similar to:: - - SELECT user.id, user.name FROM user JOIN address ON user.id = address.user_id - - .. versionchanged:: 1.4 :meth:`_expression.Select.join` now creates - a :class:`_sql.Join` object between a :class:`_sql.FromClause` - source that is within the FROM clause of the existing SELECT, - and a given target :class:`_sql.FromClause`, and then adds - this :class:`_sql.Join` to the FROM clause of the newly generated - SELECT statement. This is completely reworked from the behavior - in 1.3, which would instead create a subquery of the entire - :class:`_expression.Select` and then join that subquery to the - target. - - This is a **backwards incompatible change** as the previous behavior - was mostly useless, producing an unnamed subquery rejected by - most databases in any case. The new behavior is modeled after - that of the very successful :meth:`_orm.Query.join` method in the - ORM, in order to support the functionality of :class:`_orm.Query` - being available by using a :class:`_sql.Select` object with an - :class:`_orm.Session`. - - See the notes for this change at :ref:`change_select_join`. - - - :param target: target table to join towards - - :param onclause: ON clause of the join. If omitted, an ON clause - is generated automatically based on the :class:`_schema.ForeignKey` - linkages between the two tables, if one can be unambiguously - determined, otherwise an error is raised. - - :param isouter: if True, generate LEFT OUTER join. Same as - :meth:`_expression.Select.outerjoin`. - - :param full: if True, generate FULL OUTER join. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join_from` - - :meth:`_expression.Select.outerjoin` - - """ # noqa: E501 - join_target = coercions.expect( - roles.JoinTargetRole, target, apply_propagate_attrs=self - ) - if onclause is not None: - onclause_element = coercions.expect(roles.OnClauseRole, onclause) - else: - onclause_element = None - - self._setup_joins += ( - ( - join_target, - onclause_element, - None, - {"isouter": isouter, "full": full}, - ), - ) - return self - - def outerjoin_from( - self, - from_: _FromClauseArgument, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - full: bool = False, - ) -> Self: - r"""Create a SQL LEFT OUTER JOIN against this - :class:`_expression.Select` object's criterion and apply generatively, - returning the newly resulting :class:`_expression.Select`. - - Usage is the same as that of :meth:`_selectable.Select.join_from`. - - """ - return self.join_from( - from_, target, onclause=onclause, isouter=True, full=full - ) - - @_generative - def join_from( - self, - from_: _FromClauseArgument, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - isouter: bool = False, - full: bool = False, - ) -> Self: - r"""Create a SQL JOIN against this :class:`_expression.Select` - object's criterion - and apply generatively, returning the newly resulting - :class:`_expression.Select`. - - E.g.:: - - stmt = select(user_table, address_table).join_from( - user_table, address_table, user_table.c.id == address_table.c.user_id - ) - - The above statement generates SQL similar to:: - - SELECT user.id, user.name, address.id, address.email, address.user_id - FROM user JOIN address ON user.id = address.user_id - - .. versionadded:: 1.4 - - :param from\_: the left side of the join, will be rendered in the - FROM clause and is roughly equivalent to using the - :meth:`.Select.select_from` method. - - :param target: target table to join towards - - :param onclause: ON clause of the join. - - :param isouter: if True, generate LEFT OUTER join. Same as - :meth:`_expression.Select.outerjoin`. - - :param full: if True, generate FULL OUTER join. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join` - - """ # noqa: E501 - - # note the order of parsing from vs. target is important here, as we - # are also deriving the source of the plugin (i.e. the subject mapper - # in an ORM query) which should favor the "from_" over the "target" - - from_ = coercions.expect( - roles.FromClauseRole, from_, apply_propagate_attrs=self - ) - join_target = coercions.expect( - roles.JoinTargetRole, target, apply_propagate_attrs=self - ) - if onclause is not None: - onclause_element = coercions.expect(roles.OnClauseRole, onclause) - else: - onclause_element = None - - self._setup_joins += ( - ( - join_target, - onclause_element, - from_, - {"isouter": isouter, "full": full}, - ), - ) - return self - - def outerjoin( - self, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - full: bool = False, - ) -> Self: - """Create a left outer join. - - Parameters are the same as that of :meth:`_expression.Select.join`. - - .. versionchanged:: 1.4 :meth:`_expression.Select.outerjoin` now - creates a :class:`_sql.Join` object between a - :class:`_sql.FromClause` source that is within the FROM clause of - the existing SELECT, and a given target :class:`_sql.FromClause`, - and then adds this :class:`_sql.Join` to the FROM clause of the - newly generated SELECT statement. This is completely reworked - from the behavior in 1.3, which would instead create a subquery of - the entire - :class:`_expression.Select` and then join that subquery to the - target. - - This is a **backwards incompatible change** as the previous behavior - was mostly useless, producing an unnamed subquery rejected by - most databases in any case. The new behavior is modeled after - that of the very successful :meth:`_orm.Query.join` method in the - ORM, in order to support the functionality of :class:`_orm.Query` - being available by using a :class:`_sql.Select` object with an - :class:`_orm.Session`. - - See the notes for this change at :ref:`change_select_join`. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join` - - """ - return self.join(target, onclause=onclause, isouter=True, full=full) - - def get_final_froms(self) -> Sequence[FromClause]: - """Compute the final displayed list of :class:`_expression.FromClause` - elements. - - This method will run through the full computation required to - determine what FROM elements will be displayed in the resulting - SELECT statement, including shadowing individual tables with - JOIN objects, as well as full computation for ORM use cases including - eager loading clauses. - - For ORM use, this accessor returns the **post compilation** - list of FROM objects; this collection will include elements such as - eagerly loaded tables and joins. The objects will **not** be - ORM enabled and not work as a replacement for the - :meth:`_sql.Select.select_froms` collection; additionally, the - method is not well performing for an ORM enabled statement as it - will incur the full ORM construction process. - - To retrieve the FROM list that's implied by the "columns" collection - passed to the :class:`_sql.Select` originally, use the - :attr:`_sql.Select.columns_clause_froms` accessor. - - To select from an alternative set of columns while maintaining the - FROM list, use the :meth:`_sql.Select.with_only_columns` method and - pass the - :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter. - - .. versionadded:: 1.4.23 - the :meth:`_sql.Select.get_final_froms` - method replaces the previous :attr:`_sql.Select.froms` accessor, - which is deprecated. - - .. seealso:: - - :attr:`_sql.Select.columns_clause_froms` - - """ - - return self._compile_state_factory(self, None)._get_display_froms() - - @property - @util.deprecated( - "1.4.23", - "The :attr:`_expression.Select.froms` attribute is moved to " - "the :meth:`_expression.Select.get_final_froms` method.", - ) - def froms(self) -> Sequence[FromClause]: - """Return the displayed list of :class:`_expression.FromClause` - elements. - - - """ - return self.get_final_froms() - - @property - def columns_clause_froms(self) -> List[FromClause]: - """Return the set of :class:`_expression.FromClause` objects implied - by the columns clause of this SELECT statement. - - .. versionadded:: 1.4.23 - - .. seealso:: - - :attr:`_sql.Select.froms` - "final" FROM list taking the full - statement into account - - :meth:`_sql.Select.with_only_columns` - makes use of this - collection to set up a new FROM list - - """ - - return SelectState.get_plugin_class(self).get_columns_clause_froms( - self - ) - - @property - def inner_columns(self) -> _SelectIterable: - """An iterator of all :class:`_expression.ColumnElement` - expressions which would - be rendered into the columns clause of the resulting SELECT statement. - - This method is legacy as of 1.4 and is superseded by the - :attr:`_expression.Select.exported_columns` collection. - - """ - - return iter(self._all_selected_columns) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - if fromclause is not None and self in fromclause._cloned_set: - return True - - for f in self._iterate_from_elements(): - if f.is_derived_from(fromclause): - return True - return False - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - # Select() object has been cloned and probably adapted by the - # given clone function. Apply the cloning function to internal - # objects - - # 1. keep a dictionary of the froms we've cloned, and what - # they've become. This allows us to ensure the same cloned from - # is used when other items such as columns are "cloned" - - all_the_froms = set( - itertools.chain( - _from_objects(*self._raw_columns), - _from_objects(*self._where_criteria), - _from_objects(*[elem[0] for elem in self._setup_joins]), - ) - ) - - # do a clone for the froms we've gathered. what is important here - # is if any of the things we are selecting from, like tables, - # were converted into Join objects. if so, these need to be - # added to _from_obj explicitly, because otherwise they won't be - # part of the new state, as they don't associate themselves with - # their columns. - new_froms = {f: clone(f, **kw) for f in all_the_froms} - - # 2. copy FROM collections, adding in joins that we've created. - existing_from_obj = [clone(f, **kw) for f in self._from_obj] - add_froms = ( - {f for f in new_froms.values() if isinstance(f, Join)} - .difference(all_the_froms) - .difference(existing_from_obj) - ) - - self._from_obj = tuple(existing_from_obj) + tuple(add_froms) - - # 3. clone everything else, making sure we use columns - # corresponding to the froms we just made. - def replace( - obj: Union[BinaryExpression[Any], ColumnClause[Any]], - **kw: Any, - ) -> Optional[KeyedColumnElement[ColumnElement[Any]]]: - if isinstance(obj, ColumnClause) and obj.table in new_froms: - newelem = new_froms[obj.table].corresponding_column(obj) - return newelem - return None - - kw["replace"] = replace - - # copy everything else. for table-ish things like correlate, - # correlate_except, setup_joins, these clone normally. For - # column-expression oriented things like raw_columns, where_criteria, - # order by, we get this from the new froms. - super()._copy_internals(clone=clone, omit_attrs=("_from_obj",), **kw) - - self._reset_memoizations() - - def get_children(self, **kw: Any) -> Iterable[ClauseElement]: - return itertools.chain( - super().get_children( - omit_attrs=("_from_obj", "_correlate", "_correlate_except"), - **kw, - ), - self._iterate_from_elements(), - ) - - @_generative - def add_columns( - self, *entities: _ColumnsClauseArgument[Any] - ) -> Select[Any]: - r"""Return a new :func:`_expression.select` construct with - the given entities appended to its columns clause. - - E.g.:: - - my_select = my_select.add_columns(table.c.new_column) - - The original expressions in the columns clause remain in place. - To replace the original expressions with new ones, see the method - :meth:`_expression.Select.with_only_columns`. - - :param \*entities: column, table, or other entity expressions to be - added to the columns clause - - .. seealso:: - - :meth:`_expression.Select.with_only_columns` - replaces existing - expressions rather than appending. - - :ref:`orm_queryguide_select_multiple_entities` - ORM-centric - example - - """ - self._reset_memoizations() - - self._raw_columns = self._raw_columns + [ - coercions.expect( - roles.ColumnsClauseRole, column, apply_propagate_attrs=self - ) - for column in entities - ] - return self - - def _set_entities( - self, entities: Iterable[_ColumnsClauseArgument[Any]] - ) -> None: - self._raw_columns = [ - coercions.expect( - roles.ColumnsClauseRole, ent, apply_propagate_attrs=self - ) - for ent in util.to_list(entities) - ] - - @util.deprecated( - "1.4", - "The :meth:`_expression.Select.column` method is deprecated and will " - "be removed in a future release. Please use " - ":meth:`_expression.Select.add_columns`", - ) - def column(self, column: _ColumnsClauseArgument[Any]) -> Select[Any]: - """Return a new :func:`_expression.select` construct with - the given column expression added to its columns clause. - - E.g.:: - - my_select = my_select.column(table.c.new_column) - - See the documentation for - :meth:`_expression.Select.with_only_columns` - for guidelines on adding /replacing the columns of a - :class:`_expression.Select` object. - - """ - return self.add_columns(column) - - @util.preload_module("sqlalchemy.sql.util") - def reduce_columns(self, only_synonyms: bool = True) -> Select[Any]: - """Return a new :func:`_expression.select` construct with redundantly - named, equivalently-valued columns removed from the columns clause. - - "Redundant" here means two columns where one refers to the - other either based on foreign key, or via a simple equality - comparison in the WHERE clause of the statement. The primary purpose - of this method is to automatically construct a select statement - with all uniquely-named columns, without the need to use - table-qualified labels as - :meth:`_expression.Select.set_label_style` - does. - - When columns are omitted based on foreign key, the referred-to - column is the one that's kept. When columns are omitted based on - WHERE equivalence, the first column in the columns clause is the - one that's kept. - - :param only_synonyms: when True, limit the removal of columns - to those which have the same name as the equivalent. Otherwise, - all columns that are equivalent to another are removed. - - """ - woc: Select[Any] - woc = self.with_only_columns( - *util.preloaded.sql_util.reduce_columns( - self._all_selected_columns, - only_synonyms=only_synonyms, - *(self._where_criteria + self._from_obj), - ) - ) - return woc - - # START OVERLOADED FUNCTIONS self.with_only_columns Select 8 - - # code within this block is **programmatically, - # statically generated** by tools/generate_sel_v1_overloads.py - - @overload - def with_only_columns(self, __ent0: _TCCA[_T0]) -> Select[Tuple[_T0]]: ... - - @overload - def with_only_columns( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1] - ) -> Select[Tuple[_T0, _T1]]: ... - - @overload - def with_only_columns( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2] - ) -> Select[Tuple[_T0, _T1, _T2]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - ) -> Select[Tuple[_T0, _T1, _T2, _T3]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - __ent7: _TCCA[_T7], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]: ... - - # END OVERLOADED FUNCTIONS self.with_only_columns - - @overload - def with_only_columns( - self, - *entities: _ColumnsClauseArgument[Any], - maintain_column_froms: bool = False, - **__kw: Any, - ) -> Select[Any]: ... - - @_generative - def with_only_columns( - self, - *entities: _ColumnsClauseArgument[Any], - maintain_column_froms: bool = False, - **__kw: Any, - ) -> Select[Any]: - r"""Return a new :func:`_expression.select` construct with its columns - clause replaced with the given entities. - - By default, this method is exactly equivalent to as if the original - :func:`_expression.select` had been called with the given entities. - E.g. a statement:: - - s = select(table1.c.a, table1.c.b) - s = s.with_only_columns(table1.c.b) - - should be exactly equivalent to:: - - s = select(table1.c.b) - - In this mode of operation, :meth:`_sql.Select.with_only_columns` - will also dynamically alter the FROM clause of the - statement if it is not explicitly stated. - To maintain the existing set of FROMs including those implied by the - current columns clause, add the - :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter:: - - s = select(table1.c.a, table2.c.b) - s = s.with_only_columns(table1.c.a, maintain_column_froms=True) - - The above parameter performs a transfer of the effective FROMs - in the columns collection to the :meth:`_sql.Select.select_from` - method, as though the following were invoked:: - - s = select(table1.c.a, table2.c.b) - s = s.select_from(table1, table2).with_only_columns(table1.c.a) - - The :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter makes use of the :attr:`_sql.Select.columns_clause_froms` - collection and performs an operation equivalent to the following:: - - s = select(table1.c.a, table2.c.b) - s = s.select_from(*s.columns_clause_froms).with_only_columns(table1.c.a) - - :param \*entities: column expressions to be used. - - :param maintain_column_froms: boolean parameter that will ensure the - FROM list implied from the current columns clause will be transferred - to the :meth:`_sql.Select.select_from` method first. - - .. versionadded:: 1.4.23 - - """ # noqa: E501 - - if __kw: - raise _no_kw() - - # memoizations should be cleared here as of - # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this - # is the case for now. - self._assert_no_memoizations() - - if maintain_column_froms: - self.select_from.non_generative( # type: ignore - self, *self.columns_clause_froms - ) - - # then memoize the FROMs etc. - _MemoizedSelectEntities._generate_for_statement(self) - - self._raw_columns = [ - coercions.expect(roles.ColumnsClauseRole, c) - for c in coercions._expression_collection_was_a_list( - "entities", "Select.with_only_columns", entities - ) - ] - return self - - @property - def whereclause(self) -> Optional[ColumnElement[Any]]: - """Return the completed WHERE clause for this - :class:`_expression.Select` statement. - - This assembles the current collection of WHERE criteria - into a single :class:`_expression.BooleanClauseList` construct. - - - .. versionadded:: 1.4 - - """ - - return BooleanClauseList._construct_for_whereclause( - self._where_criteria - ) - - _whereclause = whereclause - - @_generative - def where(self, *whereclause: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.select` construct with - the given expression added to - its WHERE clause, joined to the existing clause via AND, if any. - - """ - - assert isinstance(self._where_criteria, tuple) - - for criterion in whereclause: - where_criteria: ColumnElement[Any] = coercions.expect( - roles.WhereHavingRole, criterion, apply_propagate_attrs=self - ) - self._where_criteria += (where_criteria,) - return self - - @_generative - def having(self, *having: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.select` construct with - the given expression added to - its HAVING clause, joined to the existing clause via AND, if any. - - """ - - for criterion in having: - having_criteria = coercions.expect( - roles.WhereHavingRole, criterion, apply_propagate_attrs=self - ) - self._having_criteria += (having_criteria,) - return self - - @_generative - def distinct(self, *expr: _ColumnExpressionArgument[Any]) -> Self: - r"""Return a new :func:`_expression.select` construct which - will apply DISTINCT to its columns clause. - - :param \*expr: optional column expressions. When present, - the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>>)`` - construct. - - .. deprecated:: 1.4 Using \*expr in other dialects is deprecated - and will raise :class:`_exc.CompileError` in a future version. - - """ - if expr: - self._distinct = True - self._distinct_on = self._distinct_on + tuple( - coercions.expect(roles.ByOfRole, e, apply_propagate_attrs=self) - for e in expr - ) - else: - self._distinct = True - return self - - @_generative - def select_from(self, *froms: _FromClauseArgument) -> Self: - r"""Return a new :func:`_expression.select` construct with the - given FROM expression(s) - merged into its list of FROM objects. - - E.g.:: - - table1 = table('t1', column('a')) - table2 = table('t2', column('b')) - s = select(table1.c.a).\ - select_from( - table1.join(table2, table1.c.a==table2.c.b) - ) - - The "from" list is a unique set on the identity of each element, - so adding an already present :class:`_schema.Table` - or other selectable - will have no effect. Passing a :class:`_expression.Join` that refers - to an already present :class:`_schema.Table` - or other selectable will have - the effect of concealing the presence of that selectable as - an individual element in the rendered FROM list, instead - rendering it into a JOIN clause. - - While the typical purpose of :meth:`_expression.Select.select_from` - is to - replace the default, derived FROM clause with a join, it can - also be called with individual table elements, multiple times - if desired, in the case that the FROM clause cannot be fully - derived from the columns clause:: - - select(func.count('*')).select_from(table1) - - """ - - self._from_obj += tuple( - coercions.expect( - roles.FromClauseRole, fromclause, apply_propagate_attrs=self - ) - for fromclause in froms - ) - return self - - @_generative - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.Select` - which will correlate the given FROM - clauses to that of an enclosing :class:`_expression.Select`. - - Calling this method turns off the :class:`_expression.Select` object's - default behavior of "auto-correlation". Normally, FROM elements - which appear in a :class:`_expression.Select` - that encloses this one via - its :term:`WHERE clause`, ORDER BY, HAVING or - :term:`columns clause` will be omitted from this - :class:`_expression.Select` - object's :term:`FROM clause`. - Setting an explicit correlation collection using the - :meth:`_expression.Select.correlate` - method provides a fixed list of FROM objects - that can potentially take place in this process. - - When :meth:`_expression.Select.correlate` - is used to apply specific FROM clauses - for correlation, the FROM elements become candidates for - correlation regardless of how deeply nested this - :class:`_expression.Select` - object is, relative to an enclosing :class:`_expression.Select` - which refers to - the same FROM object. This is in contrast to the behavior of - "auto-correlation" which only correlates to an immediate enclosing - :class:`_expression.Select`. - Multi-level correlation ensures that the link - between enclosed and enclosing :class:`_expression.Select` - is always via - at least one WHERE/ORDER BY/HAVING/columns clause in order for - correlation to take place. - - If ``None`` is passed, the :class:`_expression.Select` - object will correlate - none of its FROM entries, and all will render unconditionally - in the local FROM clause. - - :param \*fromclauses: one or more :class:`.FromClause` or other - FROM-compatible construct such as an ORM mapped entity to become part - of the correlate collection; alternatively pass a single value - ``None`` to remove all existing correlations. - - .. seealso:: - - :meth:`_expression.Select.correlate_except` - - :ref:`tutorial_scalar_subquery` - - """ - - # tests failing when we try to change how these - # arguments are passed - - self._auto_correlate = False - if not fromclauses or fromclauses[0] in {None, False}: - if len(fromclauses) > 1: - raise exc.ArgumentError( - "additional FROM objects not accepted when " - "passing None/False to correlate()" - ) - self._correlate = () - else: - self._correlate = self._correlate + tuple( - coercions.expect(roles.FromClauseRole, f) for f in fromclauses - ) - return self - - @_generative - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.Select` - which will omit the given FROM - clauses from the auto-correlation process. - - Calling :meth:`_expression.Select.correlate_except` turns off the - :class:`_expression.Select` object's default behavior of - "auto-correlation" for the given FROM elements. An element - specified here will unconditionally appear in the FROM list, while - all other FROM elements remain subject to normal auto-correlation - behaviors. - - If ``None`` is passed, or no arguments are passed, - the :class:`_expression.Select` object will correlate all of its - FROM entries. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate-exception collection. - - .. seealso:: - - :meth:`_expression.Select.correlate` - - :ref:`tutorial_scalar_subquery` - - """ - - self._auto_correlate = False - if not fromclauses or fromclauses[0] in {None, False}: - if len(fromclauses) > 1: - raise exc.ArgumentError( - "additional FROM objects not accepted when " - "passing None/False to correlate_except()" - ) - self._correlate_except = () - else: - self._correlate_except = (self._correlate_except or ()) + tuple( - coercions.expect(roles.FromClauseRole, f) for f in fromclauses - ) - - return self - - @HasMemoized_ro_memoized_attribute - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - For a :func:`_expression.select` construct, the collection here is - exactly what would be rendered inside the "SELECT" statement, and the - :class:`_expression.ColumnElement` objects are directly present as they - were given, e.g.:: - - col1 = column('q', Integer) - col2 = column('p', Integer) - stmt = select(col1, col2) - - Above, ``stmt.selected_columns`` would be a collection that contains - the ``col1`` and ``col2`` objects directly. For a statement that is - against a :class:`_schema.Table` or other - :class:`_expression.FromClause`, the collection will use the - :class:`_expression.ColumnElement` objects that are in the - :attr:`_expression.FromClause.c` collection of the from element. - - A use case for the :attr:`_sql.Select.selected_columns` collection is - to allow the existing columns to be referenced when adding additional - criteria, e.g.:: - - def filter_on_id(my_select, id): - return my_select.where(my_select.selected_columns['id'] == id) - - stmt = select(MyModel) - - # adds "WHERE id=:param" to the statement - stmt = filter_on_id(stmt, 42) - - .. note:: - - The :attr:`_sql.Select.selected_columns` collection does not - include expressions established in the columns clause using the - :func:`_sql.text` construct; these are silently omitted from the - collection. To use plain textual column expressions inside of a - :class:`_sql.Select` construct, use the :func:`_sql.literal_column` - construct. - - - .. versionadded:: 1.4 - - """ - - # compare to SelectState._generate_columns_plus_names, which - # generates the actual names used in the SELECT string. that - # method is more complex because it also renders columns that are - # fully ambiguous, e.g. same column more than once. - conv = cast( - "Callable[[Any], str]", - SelectState._column_naming_convention(self._label_style), - ) - - cc: ColumnCollection[str, ColumnElement[Any]] = ColumnCollection( - [ - (conv(c), c) - for c in self._all_selected_columns - if is_column_element(c) - ] - ) - return cc.as_readonly() - - @HasMemoized_ro_memoized_attribute - def _all_selected_columns(self) -> _SelectIterable: - meth = SelectState.get_plugin_class(self).all_selected_columns - return list(meth(self)) - - def _ensure_disambiguated_names(self) -> Select[Any]: - if self._label_style is LABEL_STYLE_NONE: - self = self.set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY) - return self - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - """Generate column proxies to place in the exported ``.c`` - collection of a subquery.""" - - if proxy_compound_columns: - extra_col_iterator = proxy_compound_columns - prox = [ - c._make_proxy( - subquery, - key=proxy_key, - name=required_label_name, - name_is_truncatable=True, - compound_select_cols=extra_cols, - ) - for ( - ( - required_label_name, - proxy_key, - fallback_label_name, - c, - repeated, - ), - extra_cols, - ) in ( - zip( - self._generate_columns_plus_names(False), - extra_col_iterator, - ) - ) - if is_column_element(c) - ] - else: - prox = [ - c._make_proxy( - subquery, - key=proxy_key, - name=required_label_name, - name_is_truncatable=True, - ) - for ( - required_label_name, - proxy_key, - fallback_label_name, - c, - repeated, - ) in (self._generate_columns_plus_names(False)) - if is_column_element(c) - ] - - subquery._columns._populate_separate_keys(prox) - - def _needs_parens_for_grouping(self) -> bool: - return self._has_row_limiting_clause or bool( - self._order_by_clause.clauses - ) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> Union[SelectStatementGrouping[Self], Self]: - ... - """Return a 'grouping' construct as per the - :class:`_expression.ClauseElement` specification. - - This produces an element that can be embedded in an expression. Note - that this method is called automatically as needed when constructing - expressions and should not require explicit use. - - """ - if ( - isinstance(against, CompoundSelect) - and not self._needs_parens_for_grouping() - ): - return self - else: - return SelectStatementGrouping(self) - - def union( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``UNION`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_union(self, *other) - - def union_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``UNION ALL`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_union_all(self, *other) - - def except_( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``EXCEPT`` of this select() construct against - the given selectable provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - """ - return CompoundSelect._create_except(self, *other) - - def except_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``EXCEPT ALL`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - """ - return CompoundSelect._create_except_all(self, *other) - - def intersect( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``INTERSECT`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_intersect(self, *other) - - def intersect_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``INTERSECT ALL`` of this select() construct - against the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_intersect_all(self, *other) - - -class ScalarSelect( - roles.InElementRole, Generative, GroupedElement, ColumnElement[_T] -): - """Represent a scalar subquery. - - - A :class:`_sql.ScalarSelect` is created by invoking the - :meth:`_sql.SelectBase.scalar_subquery` method. The object - then participates in other SQL expressions as a SQL column expression - within the :class:`_sql.ColumnElement` hierarchy. - - .. seealso:: - - :meth:`_sql.SelectBase.scalar_subquery` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - """ - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("type", InternalTraversal.dp_type), - ] - - _from_objects: List[FromClause] = [] - _is_from_container = True - if not TYPE_CHECKING: - _is_implicitly_boolean = False - inherit_cache = True - - element: SelectBase - - def __init__(self, element: SelectBase) -> None: - self.element = element - self.type = element._scalar_type() - self._propagate_attrs = element._propagate_attrs - - def __getattr__(self, attr: str) -> Any: - return getattr(self.element, attr) - - def __getstate__(self) -> Dict[str, Any]: - return {"element": self.element, "type": self.type} - - def __setstate__(self, state: Dict[str, Any]) -> None: - self.element = state["element"] - self.type = state["type"] - - @property - def columns(self) -> NoReturn: - raise exc.InvalidRequestError( - "Scalar Select expression has no " - "columns; use this object directly " - "within a column-level expression." - ) - - c = columns - - @_generative - def where(self, crit: _ColumnExpressionArgument[bool]) -> Self: - """Apply a WHERE clause to the SELECT statement referred to - by this :class:`_expression.ScalarSelect`. - - """ - self.element = cast("Select[Any]", self.element).where(crit) - return self - - @overload - def self_group( - self: ScalarSelect[Any], against: Optional[OperatorType] = None - ) -> ScalarSelect[Any]: ... - - @overload - def self_group( - self: ColumnElement[Any], against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: ... - - def self_group( - self, against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: - return self - - if TYPE_CHECKING: - - def _ungroup(self) -> Select[Any]: ... - - @_generative - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.ScalarSelect` - which will correlate the given FROM - clauses to that of an enclosing :class:`_expression.Select`. - - This method is mirrored from the :meth:`_sql.Select.correlate` method - of the underlying :class:`_sql.Select`. The method applies the - :meth:_sql.Select.correlate` method, then returns a new - :class:`_sql.ScalarSelect` against that statement. - - .. versionadded:: 1.4 Previously, the - :meth:`_sql.ScalarSelect.correlate` - method was only available from :class:`_sql.Select`. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate collection. - - .. seealso:: - - :meth:`_expression.ScalarSelect.correlate_except` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - - """ - self.element = cast("Select[Any]", self.element).correlate( - *fromclauses - ) - return self - - @_generative - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.ScalarSelect` - which will omit the given FROM - clauses from the auto-correlation process. - - This method is mirrored from the - :meth:`_sql.Select.correlate_except` method of the underlying - :class:`_sql.Select`. The method applies the - :meth:_sql.Select.correlate_except` method, then returns a new - :class:`_sql.ScalarSelect` against that statement. - - .. versionadded:: 1.4 Previously, the - :meth:`_sql.ScalarSelect.correlate_except` - method was only available from :class:`_sql.Select`. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate-exception collection. - - .. seealso:: - - :meth:`_expression.ScalarSelect.correlate` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - - """ - - self.element = cast("Select[Any]", self.element).correlate_except( - *fromclauses - ) - return self - - -class Exists(UnaryExpression[bool]): - """Represent an ``EXISTS`` clause. - - See :func:`_sql.exists` for a description of usage. - - An ``EXISTS`` clause can also be constructed from a :func:`_sql.select` - instance by calling :meth:`_sql.SelectBase.exists`. - - """ - - inherit_cache = True - element: Union[SelectStatementGrouping[Select[Any]], ScalarSelect[Any]] - - def __init__( - self, - __argument: Optional[ - Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]] - ] = None, - ): - s: ScalarSelect[Any] - - # TODO: this seems like we should be using coercions for this - if __argument is None: - s = Select(literal_column("*")).scalar_subquery() - elif isinstance(__argument, SelectBase): - s = __argument.scalar_subquery() - s._propagate_attrs = __argument._propagate_attrs - elif isinstance(__argument, ScalarSelect): - s = __argument - else: - s = Select(__argument).scalar_subquery() - - UnaryExpression.__init__( - self, - s, - operator=operators.exists, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=True, - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [] - - def _regroup( - self, fn: Callable[[Select[Any]], Select[Any]] - ) -> SelectStatementGrouping[Select[Any]]: - element = self.element._ungroup() - new_element = fn(element) - - return_value = new_element.self_group(against=operators.exists) - assert isinstance(return_value, SelectStatementGrouping) - return return_value - - def select(self) -> Select[Any]: - r"""Return a SELECT of this :class:`_expression.Exists`. - - e.g.:: - - stmt = exists(some_table.c.id).where(some_table.c.id == 5).select() - - This will produce a statement resembling:: - - SELECT EXISTS (SELECT id FROM some_table WHERE some_table = :param) AS anon_1 - - .. seealso:: - - :func:`_expression.select` - general purpose - method which allows for arbitrary column lists. - - """ # noqa - - return Select(self) - - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - """Apply correlation to the subquery noted by this - :class:`_sql.Exists`. - - .. seealso:: - - :meth:`_sql.ScalarSelect.correlate` - - """ - e = self._clone() - e.element = self._regroup( - lambda element: element.correlate(*fromclauses) - ) - return e - - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - """Apply correlation to the subquery noted by this - :class:`_sql.Exists`. - - .. seealso:: - - :meth:`_sql.ScalarSelect.correlate_except` - - """ - - e = self._clone() - e.element = self._regroup( - lambda element: element.correlate_except(*fromclauses) - ) - return e - - def select_from(self, *froms: _FromClauseArgument) -> Self: - """Return a new :class:`_expression.Exists` construct, - applying the given - expression to the :meth:`_expression.Select.select_from` - method of the select - statement contained. - - .. note:: it is typically preferable to build a :class:`_sql.Select` - statement first, including the desired WHERE clause, then use the - :meth:`_sql.SelectBase.exists` method to produce an - :class:`_sql.Exists` object at once. - - """ - e = self._clone() - e.element = self._regroup(lambda element: element.select_from(*froms)) - return e - - def where(self, *clause: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.exists` construct with the - given expression added to - its WHERE clause, joined to the existing clause via AND, if any. - - - .. note:: it is typically preferable to build a :class:`_sql.Select` - statement first, including the desired WHERE clause, then use the - :meth:`_sql.SelectBase.exists` method to produce an - :class:`_sql.Exists` object at once. - - """ - e = self._clone() - e.element = self._regroup(lambda element: element.where(*clause)) - return e - - -class TextualSelect(SelectBase, ExecutableReturnsRows, Generative): - """Wrap a :class:`_expression.TextClause` construct within a - :class:`_expression.SelectBase` - interface. - - This allows the :class:`_expression.TextClause` object to gain a - ``.c`` collection - and other FROM-like capabilities such as - :meth:`_expression.FromClause.alias`, - :meth:`_expression.SelectBase.cte`, etc. - - The :class:`_expression.TextualSelect` construct is produced via the - :meth:`_expression.TextClause.columns` - method - see that method for details. - - .. versionchanged:: 1.4 the :class:`_expression.TextualSelect` - class was renamed - from ``TextAsFrom``, to more correctly suit its role as a - SELECT-oriented object and not a FROM clause. - - .. seealso:: - - :func:`_expression.text` - - :meth:`_expression.TextClause.columns` - primary creation interface. - - """ - - __visit_name__ = "textual_select" - - _label_style = LABEL_STYLE_NONE - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("column_args", InternalTraversal.dp_clauseelement_list), - ] + SupportsCloneAnnotations._clone_annotations_traverse_internals - - _is_textual = True - - is_text = True - is_select = True - - def __init__( - self, - text: TextClause, - columns: List[_ColumnExpressionArgument[Any]], - positional: bool = False, - ) -> None: - self._init( - text, - # convert for ORM attributes->columns, etc - [ - coercions.expect(roles.LabeledColumnExprRole, c) - for c in columns - ], - positional, - ) - - def _init( - self, - text: TextClause, - columns: List[NamedColumn[Any]], - positional: bool = False, - ) -> None: - self.element = text - self.column_args = columns - self.positional = positional - - @HasMemoized_ro_memoized_attribute - def selected_columns( - self, - ) -> ColumnCollection[str, KeyedColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - For a :class:`_expression.TextualSelect` construct, the collection - contains the :class:`_expression.ColumnElement` objects that were - passed to the constructor, typically via the - :meth:`_expression.TextClause.columns` method. - - - .. versionadded:: 1.4 - - """ - return ColumnCollection( - (c.key, c) for c in self.column_args - ).as_readonly() - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.column_args - - def set_label_style(self, style: SelectLabelStyle) -> TextualSelect: - return self - - def _ensure_disambiguated_names(self) -> TextualSelect: - return self - - @_generative - def bindparams( - self, - *binds: BindParameter[Any], - **bind_as_values: Any, - ) -> Self: - self.element = self.element.bindparams(*binds, **bind_as_values) - return self - - def _generate_fromclause_column_proxies( - self, - fromclause: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - if TYPE_CHECKING: - assert isinstance(fromclause, Subquery) - - if proxy_compound_columns: - fromclause._columns._populate_separate_keys( - c._make_proxy(fromclause, compound_select_cols=extra_cols) - for c, extra_cols in zip( - self.column_args, proxy_compound_columns - ) - ) - else: - fromclause._columns._populate_separate_keys( - c._make_proxy(fromclause) for c in self.column_args - ) - - def _scalar_type(self) -> Union[TypeEngine[Any], Any]: - return self.column_args[0].type - - -TextAsFrom = TextualSelect -"""Backwards compatibility with the previous name""" - - -class AnnotatedFromClause(Annotated): - def _copy_internals(self, **kw: Any) -> None: - super()._copy_internals(**kw) - if kw.get("ind_cols_on_fromclause", False): - ee = self._Annotated__element # type: ignore - - self.c = ee.__class__.c.fget(self) # type: ignore - - @util.ro_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """proxy the .c collection of the underlying FromClause. - - Originally implemented in 2008 as a simple load of the .c collection - when the annotated construct was created (see d3621ae961a), in modern - SQLAlchemy versions this can be expensive for statements constructed - with ORM aliases. So for #8796 SQLAlchemy 2.0 we instead proxy - it, which works just as well. - - Two different use cases seem to require the collection either copied - from the underlying one, or unique to this AnnotatedFromClause. - - See test_selectable->test_annotated_corresponding_column - - """ - ee = self._Annotated__element # type: ignore - return ee.c # type: ignore |