Update 2025-04-13_16:25:39

This commit is contained in:
root
2025-04-13 16:25:41 +02:00
commit 4c711360d3
2979 changed files with 666585 additions and 0 deletions

View File

@ -0,0 +1,145 @@
# sql/__init__.py
# Copyright (C) 2005-2025 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
from typing import Any
from typing import TYPE_CHECKING
from ._typing import ColumnExpressionArgument as ColumnExpressionArgument
from ._typing import NotNullable as NotNullable
from ._typing import Nullable as Nullable
from .base import Executable as Executable
from .compiler import COLLECT_CARTESIAN_PRODUCTS as COLLECT_CARTESIAN_PRODUCTS
from .compiler import FROM_LINTING as FROM_LINTING
from .compiler import NO_LINTING as NO_LINTING
from .compiler import WARN_LINTING as WARN_LINTING
from .ddl import BaseDDLElement as BaseDDLElement
from .ddl import DDL as DDL
from .ddl import DDLElement as DDLElement
from .ddl import ExecutableDDLElement as ExecutableDDLElement
from .expression import Alias as Alias
from .expression import alias as alias
from .expression import all_ as all_
from .expression import and_ as and_
from .expression import any_ as any_
from .expression import asc as asc
from .expression import between as between
from .expression import bindparam as bindparam
from .expression import case as case
from .expression import cast as cast
from .expression import ClauseElement as ClauseElement
from .expression import collate as collate
from .expression import column as column
from .expression import ColumnCollection as ColumnCollection
from .expression import ColumnElement as ColumnElement
from .expression import CompoundSelect as CompoundSelect
from .expression import cte as cte
from .expression import Delete as Delete
from .expression import delete as delete
from .expression import desc as desc
from .expression import distinct as distinct
from .expression import except_ as except_
from .expression import except_all as except_all
from .expression import exists as exists
from .expression import extract as extract
from .expression import false as false
from .expression import False_ as False_
from .expression import FromClause as FromClause
from .expression import func as func
from .expression import funcfilter as funcfilter
from .expression import Insert as Insert
from .expression import insert as insert
from .expression import intersect as intersect
from .expression import intersect_all as intersect_all
from .expression import Join as Join
from .expression import join as join
from .expression import label as label
from .expression import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
from .expression import (
LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
)
from .expression import LABEL_STYLE_NONE as LABEL_STYLE_NONE
from .expression import (
LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
)
from .expression import lambda_stmt as lambda_stmt
from .expression import LambdaElement as LambdaElement
from .expression import lateral as lateral
from .expression import literal as literal
from .expression import literal_column as literal_column
from .expression import modifier as modifier
from .expression import not_ as not_
from .expression import null as null
from .expression import nulls_first as nulls_first
from .expression import nulls_last as nulls_last
from .expression import nullsfirst as nullsfirst
from .expression import nullslast as nullslast
from .expression import or_ as or_
from .expression import outerjoin as outerjoin
from .expression import outparam as outparam
from .expression import over as over
from .expression import quoted_name as quoted_name
from .expression import Select as Select
from .expression import select as select
from .expression import Selectable as Selectable
from .expression import SelectLabelStyle as SelectLabelStyle
from .expression import SQLColumnExpression as SQLColumnExpression
from .expression import StatementLambdaElement as StatementLambdaElement
from .expression import Subquery as Subquery
from .expression import table as table
from .expression import TableClause as TableClause
from .expression import TableSample as TableSample
from .expression import tablesample as tablesample
from .expression import text as text
from .expression import true as true
from .expression import True_ as True_
from .expression import try_cast as try_cast
from .expression import tuple_ as tuple_
from .expression import type_coerce as type_coerce
from .expression import union as union
from .expression import union_all as union_all
from .expression import Update as Update
from .expression import update as update
from .expression import Values as Values
from .expression import values as values
from .expression import within_group as within_group
from .visitors import ClauseVisitor as ClauseVisitor
def __go(lcls: Any) -> None:
from .. import util as _sa_util
from . import base
from . import coercions
from . import elements
from . import lambdas
from . import selectable
from . import schema
from . import traversals
from . import type_api
if not TYPE_CHECKING:
base.coercions = elements.coercions = coercions
base.elements = elements
base.type_api = type_api
coercions.elements = elements
coercions.lambdas = lambdas
coercions.schema = schema
coercions.selectable = selectable
from .annotation import _prepare_annotations
from .annotation import Annotated
from .elements import AnnotatedColumnElement
from .elements import ClauseList
from .selectable import AnnotatedFromClause
_prepare_annotations(ColumnElement, AnnotatedColumnElement)
_prepare_annotations(FromClause, AnnotatedFromClause)
_prepare_annotations(ClauseList, Annotated)
_sa_util.preloaded.import_prefix("sqlalchemy.sql")
__go(locals())

View File

@ -0,0 +1,132 @@
# sql/_dml_constructors.py
# Copyright (C) 2005-2025 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
from __future__ import annotations
from typing import TYPE_CHECKING
from .dml import Delete
from .dml import Insert
from .dml import Update
if TYPE_CHECKING:
from ._typing import _DMLTableArgument
def insert(table: _DMLTableArgument) -> Insert:
"""Construct an :class:`_expression.Insert` object.
E.g.::
from sqlalchemy import insert
stmt = insert(user_table).values(name="username", fullname="Full Username")
Similar functionality is available via the
:meth:`_expression.TableClause.insert` method on
:class:`_schema.Table`.
.. seealso::
:ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
:param table: :class:`_expression.TableClause`
which is the subject of the
insert.
:param values: collection of values to be inserted; see
:meth:`_expression.Insert.values`
for a description of allowed formats here.
Can be omitted entirely; a :class:`_expression.Insert` construct
will also dynamically render the VALUES clause at execution time
based on the parameters passed to :meth:`_engine.Connection.execute`.
:param inline: if True, no attempt will be made to retrieve the
SQL-generated default values to be provided within the statement;
in particular,
this allows SQL expressions to be rendered 'inline' within the
statement without the need to pre-execute them beforehand; for
backends that support "returning", this turns off the "implicit
returning" feature for the statement.
If both :paramref:`_expression.insert.values` and compile-time bind
parameters are present, the compile-time bind parameters override the
information specified within :paramref:`_expression.insert.values` on a
per-key basis.
The keys within :paramref:`_expression.Insert.values` can be either
:class:`~sqlalchemy.schema.Column` objects or their string
identifiers. Each key may reference one of:
* a literal data value (i.e. string, number, etc.);
* a Column object;
* a SELECT statement.
If a ``SELECT`` statement is specified which references this
``INSERT`` statement's table, the statement will be correlated
against the ``INSERT`` statement.
.. seealso::
:ref:`tutorial_core_insert` - in the :ref:`unified_tutorial`
""" # noqa: E501
return Insert(table)
def update(table: _DMLTableArgument) -> Update:
r"""Construct an :class:`_expression.Update` object.
E.g.::
from sqlalchemy import update
stmt = (
update(user_table).where(user_table.c.id == 5).values(name="user #5")
)
Similar functionality is available via the
:meth:`_expression.TableClause.update` method on
:class:`_schema.Table`.
:param table: A :class:`_schema.Table`
object representing the database
table to be updated.
.. seealso::
:ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
""" # noqa: E501
return Update(table)
def delete(table: _DMLTableArgument) -> Delete:
r"""Construct :class:`_expression.Delete` object.
E.g.::
from sqlalchemy import delete
stmt = delete(user_table).where(user_table.c.id == 5)
Similar functionality is available via the
:meth:`_expression.TableClause.delete` method on
:class:`_schema.Table`.
:param table: The table to delete rows from.
.. seealso::
:ref:`tutorial_core_update_delete` - in the :ref:`unified_tutorial`
"""
return Delete(table)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
# sql/_orm_types.py
# Copyright (C) 2022-2025 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
"""ORM types that need to present specifically for **documentation only** of
the Executable.execution_options() method, which includes options that
are meaningful to the ORM.
"""
from __future__ import annotations
from ..util.typing import Literal
SynchronizeSessionArgument = Literal[False, "auto", "evaluate", "fetch"]
DMLStrategyArgument = Literal["bulk", "raw", "orm", "auto"]

View File

@ -0,0 +1,75 @@
# sql/_py_util.py
# Copyright (C) 2005-2025 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
from __future__ import annotations
import typing
from typing import Any
from typing import Dict
from typing import Tuple
from typing import Union
from ..util.typing import Literal
if typing.TYPE_CHECKING:
from .cache_key import CacheConst
class prefix_anon_map(Dict[str, str]):
"""A map that creates new keys for missing key access.
Considers keys of the form "<ident> <name>" to produce
new symbols "<name>_<index>", where "index" is an incrementing integer
corresponding to <name>.
Inlines the approach taken by :class:`sqlalchemy.util.PopulateDict` which
is otherwise usually used for this type of operation.
"""
def __missing__(self, key: str) -> str:
(ident, derived) = key.split(" ", 1)
anonymous_counter = self.get(derived, 1)
self[derived] = anonymous_counter + 1 # type: ignore
value = f"{derived}_{anonymous_counter}"
self[key] = value
return value
class cache_anon_map(
Dict[Union[int, "Literal[CacheConst.NO_CACHE]"], Union[Literal[True], str]]
):
"""A map that creates new keys for missing key access.
Produces an incrementing sequence given a series of unique keys.
This is similar to the compiler prefix_anon_map class although simpler.
Inlines the approach taken by :class:`sqlalchemy.util.PopulateDict` which
is otherwise usually used for this type of operation.
"""
_index = 0
def get_anon(self, object_: Any) -> Tuple[str, bool]:
idself = id(object_)
if idself in self:
s_val = self[idself]
assert s_val is not True
return s_val, True
else:
# inline of __missing__
self[idself] = id_ = str(self._index)
self._index += 1
return id_, False
def __missing__(self, key: int) -> str:
self[key] = val = str(self._index)
self._index += 1
return val

View File

@ -0,0 +1,715 @@
# sql/_selectable_constructors.py
# Copyright (C) 2005-2025 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
from __future__ import annotations
from typing import Any
from typing import Optional
from typing import overload
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from . import coercions
from . import roles
from ._typing import _ColumnsClauseArgument
from ._typing import _no_kw
from .elements import ColumnClause
from .selectable import Alias
from .selectable import CompoundSelect
from .selectable import Exists
from .selectable import FromClause
from .selectable import Join
from .selectable import Lateral
from .selectable import LateralFromClause
from .selectable import NamedFromClause
from .selectable import Select
from .selectable import TableClause
from .selectable import TableSample
from .selectable import Values
if TYPE_CHECKING:
from ._typing import _FromClauseArgument
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 _T8
from ._typing import _T9
from ._typing import _TP
from ._typing import _TypedColumnClauseArgument as _TCCA
from .functions import Function
from .selectable import CTE
from .selectable import HasCTE
from .selectable import ScalarSelect
from .selectable import SelectBase
def alias(
selectable: FromClause, name: Optional[str] = None, flat: bool = False
) -> NamedFromClause:
"""Return a named alias of the given :class:`.FromClause`.
For :class:`.Table` and :class:`.Join` objects, the return type is the
:class:`_expression.Alias` object. Other kinds of :class:`.NamedFromClause`
objects may be returned for other kinds of :class:`.FromClause` objects.
The named alias represents any :class:`_expression.FromClause` with an
alternate name assigned within SQL, typically using the ``AS`` clause when
generated, e.g. ``SELECT * FROM table AS aliasname``.
Equivalent functionality is available via the
:meth:`_expression.FromClause.alias`
method available on all :class:`_expression.FromClause` objects.
:param selectable: any :class:`_expression.FromClause` subclass,
such as a table, select statement, etc.
:param name: string name to be assigned as the alias.
If ``None``, a name will be deterministically generated at compile
time. Deterministic means the name is guaranteed to be unique against
other constructs used in the same statement, and will also be the same
name for each successive compilation of the same statement object.
:param flat: Will be passed through to if the given selectable
is an instance of :class:`_expression.Join` - see
:meth:`_expression.Join.alias` for details.
"""
return Alias._factory(selectable, name=name, flat=flat)
def cte(
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
)
# TODO: mypy requires the _TypedSelectable overloads in all compound select
# constructors since _SelectStatementForCompoundArgument includes
# untyped args that make it return CompoundSelect[Unpack[tuple[Never, ...]]]
# pyright does not have this issue
_TypedSelectable = Union["Select[_TP]", "CompoundSelect[_TP]"]
@overload
def except_(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def except_(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def except_(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return an ``EXCEPT`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
:param \*selects:
a list of :class:`_expression.Select` instances.
"""
return CompoundSelect._create_except(*selects)
@overload
def except_all(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def except_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def except_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return an ``EXCEPT ALL`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
:param \*selects:
a list of :class:`_expression.Select` instances.
"""
return CompoundSelect._create_except_all(*selects)
def exists(
__argument: Optional[
Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]]
] = None,
) -> Exists:
"""Construct a new :class:`_expression.Exists` construct.
The :func:`_sql.exists` can be invoked by itself to produce an
:class:`_sql.Exists` construct, which will accept simple WHERE
criteria::
exists_criteria = exists().where(table1.c.col1 == table2.c.col2)
However, for greater flexibility in constructing the SELECT, an
existing :class:`_sql.Select` construct may be converted to an
:class:`_sql.Exists`, most conveniently by making use of the
:meth:`_sql.SelectBase.exists` method::
exists_criteria = (
select(table2.c.col2).where(table1.c.col1 == table2.c.col2).exists()
)
The EXISTS criteria is then used inside of an enclosing SELECT::
stmt = select(table1.c.col1).where(exists_criteria)
The above statement will then be of the form:
.. sourcecode:: sql
SELECT col1 FROM table1 WHERE EXISTS
(SELECT table2.col2 FROM table2 WHERE table2.col2 = table1.col1)
.. seealso::
:ref:`tutorial_exists` - in the :term:`2.0 style` tutorial.
:meth:`_sql.SelectBase.exists` - method to transform a ``SELECT`` to an
``EXISTS`` clause.
""" # noqa: E501
return Exists(__argument)
@overload
def intersect(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def intersect(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def intersect(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return an ``INTERSECT`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
:param \*selects:
a list of :class:`_expression.Select` instances.
"""
return CompoundSelect._create_intersect(*selects)
@overload
def intersect_all(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def intersect_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def intersect_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return an ``INTERSECT ALL`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
:param \*selects:
a list of :class:`_expression.Select` instances.
"""
return CompoundSelect._create_intersect_all(*selects)
def join(
left: _FromClauseArgument,
right: _FromClauseArgument,
onclause: Optional[_OnClauseArgument] = None,
isouter: bool = False,
full: bool = False,
) -> Join:
"""Produce a :class:`_expression.Join` object, given two
:class:`_expression.FromClause`
expressions.
E.g.::
j = join(
user_table, 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:
.. sourcecode:: sql
SELECT user.id, user.name FROM user
JOIN address ON user.id = address.user_id
Similar functionality is available given any
:class:`_expression.FromClause` object (e.g. such as a
:class:`_schema.Table`) using
the :meth:`_expression.FromClause.join` method.
:param left: The left side of the join.
: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 JOIN.
.. seealso::
:meth:`_expression.FromClause.join` - method form,
based on a given left side.
:class:`_expression.Join` - the type of object produced.
""" # noqa: E501
return Join(left, right, onclause, isouter, full)
def lateral(
selectable: Union[SelectBase, _FromClauseArgument],
name: Optional[str] = None,
) -> LateralFromClause:
"""Return a :class:`_expression.Lateral` object.
:class:`_expression.Lateral` is an :class:`_expression.Alias`
subclass that represents
a subquery with the LATERAL keyword applied to it.
The special behavior of a LATERAL subquery is that it appears in the
FROM clause of an enclosing SELECT, but may correlate to other
FROM clauses of that SELECT. It is a special case of subquery
only supported by a small number of backends, currently more recent
PostgreSQL versions.
.. seealso::
:ref:`tutorial_lateral_correlation` - overview of usage.
"""
return Lateral._factory(selectable, name=name)
def outerjoin(
left: _FromClauseArgument,
right: _FromClauseArgument,
onclause: Optional[_OnClauseArgument] = None,
full: bool = False,
) -> Join:
"""Return an ``OUTER JOIN`` clause element.
The returned object is an instance of :class:`_expression.Join`.
Similar functionality is also available via the
:meth:`_expression.FromClause.outerjoin` method on any
:class:`_expression.FromClause`.
:param left: The left side of the join.
:param right: The right side of the join.
:param onclause: Optional criterion for the ``ON`` clause, is
derived from foreign key relationships established between
left and right otherwise.
To chain joins together, use the :meth:`_expression.FromClause.join`
or
:meth:`_expression.FromClause.outerjoin` methods on the resulting
:class:`_expression.Join` object.
"""
return Join(left, right, onclause, isouter=True, full=full)
# START OVERLOADED FUNCTIONS select Select 1-10
# code within this block is **programmatically,
# statically generated** by tools/generate_tuple_map_overloads.py
@overload
def select(__ent0: _TCCA[_T0]) -> Select[Tuple[_T0]]: ...
@overload
def select(
__ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
) -> Select[Tuple[_T0, _T1]]: ...
@overload
def select(
__ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
) -> Select[Tuple[_T0, _T1, _T2]]: ...
@overload
def select(
__ent0: _TCCA[_T0],
__ent1: _TCCA[_T1],
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
) -> Select[Tuple[_T0, _T1, _T2, _T3]]: ...
@overload
def select(
__ent0: _TCCA[_T0],
__ent1: _TCCA[_T1],
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
__ent4: _TCCA[_T4],
) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4]]: ...
@overload
def select(
__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 select(
__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 select(
__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]]: ...
@overload
def select(
__ent0: _TCCA[_T0],
__ent1: _TCCA[_T1],
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
__ent4: _TCCA[_T4],
__ent5: _TCCA[_T5],
__ent6: _TCCA[_T6],
__ent7: _TCCA[_T7],
__ent8: _TCCA[_T8],
) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8]]: ...
@overload
def select(
__ent0: _TCCA[_T0],
__ent1: _TCCA[_T1],
__ent2: _TCCA[_T2],
__ent3: _TCCA[_T3],
__ent4: _TCCA[_T4],
__ent5: _TCCA[_T5],
__ent6: _TCCA[_T6],
__ent7: _TCCA[_T7],
__ent8: _TCCA[_T8],
__ent9: _TCCA[_T9],
) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9]]: ...
# END OVERLOADED FUNCTIONS select
@overload
def select(
*entities: _ColumnsClauseArgument[Any], **__kw: Any
) -> Select[Any]: ...
def select(*entities: _ColumnsClauseArgument[Any], **__kw: Any) -> Select[Any]:
r"""Construct a new :class:`_expression.Select`.
.. versionadded:: 1.4 - The :func:`_sql.select` function now accepts
column arguments positionally. The top-level :func:`_sql.select`
function will automatically use the 1.x or 2.x style API based on
the incoming arguments; using :func:`_sql.select` from the
``sqlalchemy.future`` module will enforce that only the 2.x style
constructor is used.
Similar functionality is also available via the
:meth:`_expression.FromClause.select` method on any
:class:`_expression.FromClause`.
.. seealso::
:ref:`tutorial_selecting_data` - in the :ref:`unified_tutorial`
:param \*entities:
Entities to SELECT from. For Core usage, this is typically a series
of :class:`_expression.ColumnElement` and / or
:class:`_expression.FromClause`
objects which will form the columns clause of the resulting
statement. For those objects that are instances of
:class:`_expression.FromClause` (typically :class:`_schema.Table`
or :class:`_expression.Alias`
objects), the :attr:`_expression.FromClause.c`
collection is extracted
to form a collection of :class:`_expression.ColumnElement` objects.
This parameter will also accept :class:`_expression.TextClause`
constructs as
given, as well as ORM-mapped classes.
"""
# the keyword args are a necessary element in order for the typing
# to work out w/ the varargs vs. having named "keyword" arguments that
# aren't always present.
if __kw:
raise _no_kw()
return Select(*entities)
def table(name: str, *columns: ColumnClause[Any], **kw: Any) -> TableClause:
"""Produce a new :class:`_expression.TableClause`.
The object returned is an instance of
:class:`_expression.TableClause`, which
represents the "syntactical" portion of the schema-level
:class:`_schema.Table` object.
It may be used to construct lightweight table constructs.
:param name: Name of the table.
:param columns: A collection of :func:`_expression.column` constructs.
:param schema: The schema name for this table.
.. versionadded:: 1.3.18 :func:`_expression.table` can now
accept a ``schema`` argument.
"""
return TableClause(name, *columns, **kw)
def tablesample(
selectable: _FromClauseArgument,
sampling: Union[float, Function[Any]],
name: Optional[str] = None,
seed: Optional[roles.ExpressionElementRole[Any]] = None,
) -> TableSample:
"""Return a :class:`_expression.TableSample` object.
:class:`_expression.TableSample` is an :class:`_expression.Alias`
subclass that represents
a table with the TABLESAMPLE clause applied to it.
:func:`_expression.tablesample`
is also available from the :class:`_expression.FromClause`
class via the
:meth:`_expression.FromClause.tablesample` method.
The TABLESAMPLE clause allows selecting a randomly selected approximate
percentage of rows from a table. It supports multiple sampling methods,
most commonly BERNOULLI and SYSTEM.
e.g.::
from sqlalchemy import func
selectable = people.tablesample(
func.bernoulli(1), name="alias", seed=func.random()
)
stmt = select(selectable.c.people_id)
Assuming ``people`` with a column ``people_id``, the above
statement would render as:
.. sourcecode:: sql
SELECT alias.people_id FROM
people AS alias TABLESAMPLE bernoulli(:bernoulli_1)
REPEATABLE (random())
:param sampling: a ``float`` percentage between 0 and 100 or
:class:`_functions.Function`.
:param name: optional alias name
:param seed: any real-valued SQL expression. When specified, the
REPEATABLE sub-clause is also rendered.
"""
return TableSample._factory(selectable, sampling, name=name, seed=seed)
@overload
def union(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def union(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def union(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return a ``UNION`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
A similar :func:`union()` method is available on all
:class:`_expression.FromClause` subclasses.
:param \*selects:
a list of :class:`_expression.Select` instances.
:param \**kwargs:
available keyword arguments are the same as those of
:func:`select`.
"""
return CompoundSelect._create_union(*selects)
@overload
def union_all(
*selects: _TypedSelectable[_TP],
) -> CompoundSelect[_TP]: ...
@overload
def union_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]: ...
def union_all(
*selects: _SelectStatementForCompoundArgument[_TP],
) -> CompoundSelect[_TP]:
r"""Return a ``UNION ALL`` of multiple selectables.
The returned object is an instance of
:class:`_expression.CompoundSelect`.
A similar :func:`union_all()` method is available on all
:class:`_expression.FromClause` subclasses.
:param \*selects:
a list of :class:`_expression.Select` instances.
"""
return CompoundSelect._create_union_all(*selects)
def values(
*columns: ColumnClause[Any],
name: Optional[str] = None,
literal_binds: bool = False,
) -> Values:
r"""Construct a :class:`_expression.Values` construct.
The column expressions and the actual data for
:class:`_expression.Values` are given in two separate steps. The
constructor receives the column expressions typically as
:func:`_expression.column` constructs,
and the data is then passed via the
:meth:`_expression.Values.data` method as a list,
which can be called multiple
times to add more data, e.g.::
from sqlalchemy import column
from sqlalchemy import values
from sqlalchemy import Integer
from sqlalchemy import String
value_expr = values(
column("id", Integer),
column("name", String),
name="my_values",
).data([(1, "name1"), (2, "name2"), (3, "name3")])
:param \*columns: column expressions, typically composed using
:func:`_expression.column` objects.
:param name: the name for this VALUES construct. If omitted, the
VALUES construct will be unnamed in a SQL expression. Different
backends may have different requirements here.
:param literal_binds: Defaults to False. Whether or not to render
the data values inline in the SQL output, rather than using bound
parameters.
"""
return Values(*columns, literal_binds=literal_binds, name=name)

View File

@ -0,0 +1,463 @@
# sql/_typing.py
# Copyright (C) 2022-2025 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
from __future__ import annotations
import operator
from typing import Any
from typing import Callable
from typing import Dict
from typing import Generic
from typing import Iterable
from typing import Mapping
from typing import NoReturn
from typing import Optional
from typing import overload
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 roles
from .. import exc
from .. import util
from ..inspection import Inspectable
from ..util.typing import Literal
from ..util.typing import Protocol
from ..util.typing import TypeAlias
if TYPE_CHECKING:
from datetime import date
from datetime import datetime
from datetime import time
from datetime import timedelta
from decimal import Decimal
from uuid import UUID
from .base import Executable
from .compiler import Compiled
from .compiler import DDLCompiler
from .compiler import SQLCompiler
from .dml import UpdateBase
from .dml import ValuesBase
from .elements import ClauseElement
from .elements import ColumnElement
from .elements import KeyedColumnElement
from .elements import quoted_name
from .elements import SQLCoreOperations
from .elements import TextClause
from .lambdas import LambdaElement
from .roles import FromClauseRole
from .schema import Column
from .selectable import Alias
from .selectable import CompoundSelect
from .selectable import CTE
from .selectable import FromClause
from .selectable import Join
from .selectable import NamedFromClause
from .selectable import ReturnsRows
from .selectable import Select
from .selectable import Selectable
from .selectable import SelectBase
from .selectable import Subquery
from .selectable import TableClause
from .sqltypes import TableValueType
from .sqltypes import TupleType
from .type_api import TypeEngine
from ..engine import Dialect
from ..util.typing import TypeGuard
_T = TypeVar("_T", bound=Any)
_T_co = TypeVar("_T_co", bound=Any, covariant=True)
_CE = TypeVar("_CE", bound="ColumnElement[Any]")
_CLE = TypeVar("_CLE", bound="ClauseElement")
class _HasClauseElement(Protocol, Generic[_T_co]):
"""indicates a class that has a __clause_element__() method"""
def __clause_element__(self) -> roles.ExpressionElementRole[_T_co]: ...
class _CoreAdapterProto(Protocol):
"""protocol for the ClauseAdapter/ColumnAdapter.traverse() method."""
def __call__(self, obj: _CE) -> _CE: ...
class _HasDialect(Protocol):
"""protocol for Engine/Connection-like objects that have dialect
attribute.
"""
@property
def dialect(self) -> Dialect: ...
# match column types that are not ORM entities
_NOT_ENTITY = TypeVar(
"_NOT_ENTITY",
int,
str,
bool,
"datetime",
"date",
"time",
"timedelta",
"UUID",
float,
"Decimal",
)
_StarOrOne = Literal["*", 1]
_MAYBE_ENTITY = TypeVar(
"_MAYBE_ENTITY",
roles.ColumnsClauseRole,
_StarOrOne,
Type[Any],
Inspectable[_HasClauseElement[Any]],
_HasClauseElement[Any],
)
# convention:
# XYZArgument - something that the end user is passing to a public API method
# XYZElement - the internal representation that we use for the thing.
# the coercions system is responsible for converting from XYZArgument to
# XYZElement.
_TextCoercedExpressionArgument = Union[
str,
"TextClause",
"ColumnElement[_T]",
_HasClauseElement[_T],
roles.ExpressionElementRole[_T],
]
_ColumnsClauseArgument = Union[
roles.TypedColumnsClauseRole[_T],
roles.ColumnsClauseRole,
"SQLCoreOperations[_T]",
_StarOrOne,
Type[_T],
Inspectable[_HasClauseElement[_T]],
_HasClauseElement[_T],
]
"""open-ended SELECT columns clause argument.
Includes column expressions, tables, ORM mapped entities, a few literal values.
This type is used for lists of columns / entities to be returned in result
sets; select(...), insert().returning(...), etc.
"""
_TypedColumnClauseArgument = Union[
roles.TypedColumnsClauseRole[_T],
"SQLCoreOperations[_T]",
Type[_T],
]
_TP = TypeVar("_TP", bound=Tuple[Any, ...])
_T0 = TypeVar("_T0", bound=Any)
_T1 = TypeVar("_T1", bound=Any)
_T2 = TypeVar("_T2", bound=Any)
_T3 = TypeVar("_T3", bound=Any)
_T4 = TypeVar("_T4", bound=Any)
_T5 = TypeVar("_T5", bound=Any)
_T6 = TypeVar("_T6", bound=Any)
_T7 = TypeVar("_T7", bound=Any)
_T8 = TypeVar("_T8", bound=Any)
_T9 = TypeVar("_T9", bound=Any)
_ColumnExpressionArgument = Union[
"ColumnElement[_T]",
_HasClauseElement[_T],
"SQLCoreOperations[_T]",
roles.ExpressionElementRole[_T],
roles.TypedColumnsClauseRole[_T],
Callable[[], "ColumnElement[_T]"],
"LambdaElement",
]
"See docs in public alias ColumnExpressionArgument."
ColumnExpressionArgument: TypeAlias = _ColumnExpressionArgument[_T]
"""Narrower "column expression" argument.
This type is used for all the other "column" kinds of expressions that
typically represent a single SQL column expression, not a set of columns the
way a table or ORM entity does.
This includes ColumnElement, or ORM-mapped attributes that will have a
``__clause_element__()`` method, it also has the ExpressionElementRole
overall which brings in the TextClause object also.
.. versionadded:: 2.0.13
"""
_ColumnExpressionOrLiteralArgument = Union[Any, _ColumnExpressionArgument[_T]]
_ColumnExpressionOrStrLabelArgument = Union[str, _ColumnExpressionArgument[_T]]
_ByArgument = Union[
Iterable[_ColumnExpressionOrStrLabelArgument[Any]],
_ColumnExpressionOrStrLabelArgument[Any],
]
"""Used for keyword-based ``order_by`` and ``partition_by`` parameters."""
_InfoType = Dict[Any, Any]
"""the .info dictionary accepted and used throughout Core /ORM"""
_FromClauseArgument = Union[
roles.FromClauseRole,
Type[Any],
Inspectable[_HasClauseElement[Any]],
_HasClauseElement[Any],
]
"""A FROM clause, like we would send to select().select_from().
Also accommodates ORM entities and related constructs.
"""
_JoinTargetArgument = Union[_FromClauseArgument, roles.JoinTargetRole]
"""target for join() builds on _FromClauseArgument to include additional
join target roles such as those which come from the ORM.
"""
_OnClauseArgument = Union[_ColumnExpressionArgument[Any], roles.OnClauseRole]
"""target for an ON clause, includes additional roles such as those which
come from the ORM.
"""
_SelectStatementForCompoundArgument = Union[
"Select[_TP]",
"CompoundSelect[_TP]",
roles.CompoundElementRole,
]
"""SELECT statement acceptable by ``union()`` and other SQL set operations"""
_DMLColumnArgument = Union[
str,
_HasClauseElement[Any],
roles.DMLColumnRole,
"SQLCoreOperations[Any]",
]
"""A DML column expression. This is a "key" inside of insert().values(),
update().values(), and related.
These are usually strings or SQL table columns.
There's also edge cases like JSON expression assignment, which we would want
the DMLColumnRole to be able to accommodate.
"""
_DMLKey = TypeVar("_DMLKey", bound=_DMLColumnArgument)
_DMLColumnKeyMapping = Mapping[_DMLKey, Any]
_DDLColumnArgument = Union[str, "Column[Any]", roles.DDLConstraintColumnRole]
"""DDL column.
used for :class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`, etc.
"""
_DMLTableArgument = Union[
"TableClause",
"Join",
"Alias",
"CTE",
Type[Any],
Inspectable[_HasClauseElement[Any]],
_HasClauseElement[Any],
]
_PropagateAttrsType = util.immutabledict[str, Any]
_TypeEngineArgument = Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]
_EquivalentColumnMap = Dict["ColumnElement[Any]", Set["ColumnElement[Any]"]]
_LimitOffsetType = Union[int, _ColumnExpressionArgument[int], None]
_AutoIncrementType = Union[bool, Literal["auto", "ignore_fk"]]
if TYPE_CHECKING:
def is_sql_compiler(c: Compiled) -> TypeGuard[SQLCompiler]: ...
def is_ddl_compiler(c: Compiled) -> TypeGuard[DDLCompiler]: ...
def is_named_from_clause(
t: FromClauseRole,
) -> TypeGuard[NamedFromClause]: ...
def is_column_element(
c: ClauseElement,
) -> TypeGuard[ColumnElement[Any]]: ...
def is_keyed_column_element(
c: ClauseElement,
) -> TypeGuard[KeyedColumnElement[Any]]: ...
def is_text_clause(c: ClauseElement) -> TypeGuard[TextClause]: ...
def is_from_clause(c: ClauseElement) -> TypeGuard[FromClause]: ...
def is_tuple_type(t: TypeEngine[Any]) -> TypeGuard[TupleType]: ...
def is_table_value_type(
t: TypeEngine[Any],
) -> TypeGuard[TableValueType]: ...
def is_selectable(t: Any) -> TypeGuard[Selectable]: ...
def is_select_base(
t: Union[Executable, ReturnsRows]
) -> TypeGuard[SelectBase]: ...
def is_select_statement(
t: Union[Executable, ReturnsRows]
) -> TypeGuard[Select[Any]]: ...
def is_table(t: FromClause) -> TypeGuard[TableClause]: ...
def is_subquery(t: FromClause) -> TypeGuard[Subquery]: ...
def is_dml(c: ClauseElement) -> TypeGuard[UpdateBase]: ...
else:
is_sql_compiler = operator.attrgetter("is_sql")
is_ddl_compiler = operator.attrgetter("is_ddl")
is_named_from_clause = operator.attrgetter("named_with_column")
is_column_element = operator.attrgetter("_is_column_element")
is_keyed_column_element = operator.attrgetter("_is_keyed_column_element")
is_text_clause = operator.attrgetter("_is_text_clause")
is_from_clause = operator.attrgetter("_is_from_clause")
is_tuple_type = operator.attrgetter("_is_tuple_type")
is_table_value_type = operator.attrgetter("_is_table_value")
is_selectable = operator.attrgetter("is_selectable")
is_select_base = operator.attrgetter("_is_select_base")
is_select_statement = operator.attrgetter("_is_select_statement")
is_table = operator.attrgetter("_is_table")
is_subquery = operator.attrgetter("_is_subquery")
is_dml = operator.attrgetter("is_dml")
def has_schema_attr(t: FromClauseRole) -> TypeGuard[TableClause]:
return hasattr(t, "schema")
def is_quoted_name(s: str) -> TypeGuard[quoted_name]:
return hasattr(s, "quote")
def is_has_clause_element(s: object) -> TypeGuard[_HasClauseElement[Any]]:
return hasattr(s, "__clause_element__")
def is_insert_update(c: ClauseElement) -> TypeGuard[ValuesBase]:
return c.is_dml and (c.is_insert or c.is_update) # type: ignore
def _no_kw() -> exc.ArgumentError:
return exc.ArgumentError(
"Additional keyword arguments are not accepted by this "
"function/method. The presence of **kw is for pep-484 typing purposes"
)
def _unexpected_kw(methname: str, kw: Dict[str, Any]) -> NoReturn:
k = list(kw)[0]
raise TypeError(f"{methname} got an unexpected keyword argument '{k}'")
@overload
def Nullable(
val: "SQLCoreOperations[_T]",
) -> "SQLCoreOperations[Optional[_T]]": ...
@overload
def Nullable(
val: roles.ExpressionElementRole[_T],
) -> roles.ExpressionElementRole[Optional[_T]]: ...
@overload
def Nullable(val: Type[_T]) -> Type[Optional[_T]]: ...
def Nullable(
val: _TypedColumnClauseArgument[_T],
) -> _TypedColumnClauseArgument[Optional[_T]]:
"""Types a column or ORM class as nullable.
This can be used in select and other contexts to express that the value of
a column can be null, for example due to an outer join::
stmt1 = select(A, Nullable(B)).outerjoin(A.bs)
stmt2 = select(A.data, Nullable(B.data)).outerjoin(A.bs)
At runtime this method returns the input unchanged.
.. versionadded:: 2.0.20
"""
return val
@overload
def NotNullable(
val: "SQLCoreOperations[Optional[_T]]",
) -> "SQLCoreOperations[_T]": ...
@overload
def NotNullable(
val: roles.ExpressionElementRole[Optional[_T]],
) -> roles.ExpressionElementRole[_T]: ...
@overload
def NotNullable(val: Type[Optional[_T]]) -> Type[_T]: ...
@overload
def NotNullable(val: Optional[Type[_T]]) -> Type[_T]: ...
def NotNullable(
val: Union[_TypedColumnClauseArgument[Optional[_T]], Optional[Type[_T]]],
) -> _TypedColumnClauseArgument[_T]:
"""Types a column or ORM class as not nullable.
This can be used in select and other contexts to express that the value of
a column cannot be null, for example due to a where condition on a
nullable column::
stmt = select(NotNullable(A.value)).where(A.value.is_not(None))
At runtime this method returns the input unchanged.
.. versionadded:: 2.0.20
"""
return val # type: ignore

View File

@ -0,0 +1,585 @@
# sql/annotation.py
# Copyright (C) 2005-2025 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:`.Annotated` class and related routines; creates hash-equivalent
copies of SQL constructs which contain context-specific markers and
associations.
Note that the :class:`.Annotated` concept as implemented in this module is not
related in any way to the pep-593 concept of "Annotated".
"""
from __future__ import annotations
import typing
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import FrozenSet
from typing import Mapping
from typing import Optional
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from . import operators
from .cache_key import HasCacheKey
from .visitors import anon_map
from .visitors import ExternallyTraversible
from .visitors import InternalTraversal
from .. import util
from ..util.typing import Literal
from ..util.typing import Self
if TYPE_CHECKING:
from .base import _EntityNamespace
from .visitors import _TraverseInternalsType
_AnnotationDict = Mapping[str, Any]
EMPTY_ANNOTATIONS: util.immutabledict[str, Any] = util.EMPTY_DICT
class SupportsAnnotations(ExternallyTraversible):
__slots__ = ()
_annotations: util.immutabledict[str, Any] = EMPTY_ANNOTATIONS
proxy_set: util.generic_fn_descriptor[FrozenSet[Any]]
_is_immutable: bool
def _annotate(self, values: _AnnotationDict) -> Self:
raise NotImplementedError()
@overload
def _deannotate(
self,
values: Literal[None] = ...,
clone: bool = ...,
) -> Self: ...
@overload
def _deannotate(
self,
values: Sequence[str] = ...,
clone: bool = ...,
) -> SupportsAnnotations: ...
def _deannotate(
self,
values: Optional[Sequence[str]] = None,
clone: bool = False,
) -> SupportsAnnotations:
raise NotImplementedError()
@util.memoized_property
def _annotations_cache_key(self) -> Tuple[Any, ...]:
anon_map_ = anon_map()
return self._gen_annotations_cache_key(anon_map_)
def _gen_annotations_cache_key(
self, anon_map: anon_map
) -> Tuple[Any, ...]:
return (
"_annotations",
tuple(
(
key,
(
value._gen_cache_key(anon_map, [])
if isinstance(value, HasCacheKey)
else value
),
)
for key, value in [
(key, self._annotations[key])
for key in sorted(self._annotations)
]
),
)
class SupportsWrappingAnnotations(SupportsAnnotations):
__slots__ = ()
_constructor: Callable[..., SupportsWrappingAnnotations]
if TYPE_CHECKING:
@util.ro_non_memoized_property
def entity_namespace(self) -> _EntityNamespace: ...
def _annotate(self, values: _AnnotationDict) -> Self:
"""return a copy of this ClauseElement with annotations
updated by the given dictionary.
"""
return Annotated._as_annotated_instance(self, values) # type: ignore
def _with_annotations(self, values: _AnnotationDict) -> Self:
"""return a copy of this ClauseElement with annotations
replaced by the given dictionary.
"""
return Annotated._as_annotated_instance(self, values) # type: ignore
@overload
def _deannotate(
self,
values: Literal[None] = ...,
clone: bool = ...,
) -> Self: ...
@overload
def _deannotate(
self,
values: Sequence[str] = ...,
clone: bool = ...,
) -> SupportsAnnotations: ...
def _deannotate(
self,
values: Optional[Sequence[str]] = None,
clone: bool = False,
) -> SupportsAnnotations:
"""return a copy of this :class:`_expression.ClauseElement`
with annotations
removed.
:param values: optional tuple of individual values
to remove.
"""
if clone:
s = self._clone()
return s
else:
return self
class SupportsCloneAnnotations(SupportsWrappingAnnotations):
# SupportsCloneAnnotations extends from SupportsWrappingAnnotations
# to support the structure of having the base ClauseElement
# be a subclass of SupportsWrappingAnnotations. Any ClauseElement
# subclass that wants to extend from SupportsCloneAnnotations
# will inherently also be subclassing SupportsWrappingAnnotations, so
# make that specific here.
if not typing.TYPE_CHECKING:
__slots__ = ()
_clone_annotations_traverse_internals: _TraverseInternalsType = [
("_annotations", InternalTraversal.dp_annotations_key)
]
def _annotate(self, values: _AnnotationDict) -> Self:
"""return a copy of this ClauseElement with annotations
updated by the given dictionary.
"""
new = self._clone()
new._annotations = new._annotations.union(values)
new.__dict__.pop("_annotations_cache_key", None)
new.__dict__.pop("_generate_cache_key", None)
return new
def _with_annotations(self, values: _AnnotationDict) -> Self:
"""return a copy of this ClauseElement with annotations
replaced by the given dictionary.
"""
new = self._clone()
new._annotations = util.immutabledict(values)
new.__dict__.pop("_annotations_cache_key", None)
new.__dict__.pop("_generate_cache_key", None)
return new
@overload
def _deannotate(
self,
values: Literal[None] = ...,
clone: bool = ...,
) -> Self: ...
@overload
def _deannotate(
self,
values: Sequence[str] = ...,
clone: bool = ...,
) -> SupportsAnnotations: ...
def _deannotate(
self,
values: Optional[Sequence[str]] = None,
clone: bool = False,
) -> SupportsAnnotations:
"""return a copy of this :class:`_expression.ClauseElement`
with annotations
removed.
:param values: optional tuple of individual values
to remove.
"""
if clone or self._annotations:
# clone is used when we are also copying
# the expression for a deep deannotation
new = self._clone()
new._annotations = util.immutabledict()
new.__dict__.pop("_annotations_cache_key", None)
return new
else:
return self
class Annotated(SupportsAnnotations):
"""clones a SupportsAnnotations and applies an 'annotations' dictionary.
Unlike regular clones, this clone also mimics __hash__() and
__eq__() of the original element so that it takes its place
in hashed collections.
A reference to the original element is maintained, for the important
reason of keeping its hash value current. When GC'ed, the
hash value may be reused, causing conflicts.
.. note:: The rationale for Annotated producing a brand new class,
rather than placing the functionality directly within ClauseElement,
is **performance**. The __hash__() method is absent on plain
ClauseElement which leads to significantly reduced function call
overhead, as the use of sets and dictionaries against ClauseElement
objects is prevalent, but most are not "annotated".
"""
_is_column_operators = False
@classmethod
def _as_annotated_instance(
cls, element: SupportsWrappingAnnotations, values: _AnnotationDict
) -> Annotated:
try:
cls = annotated_classes[element.__class__]
except KeyError:
cls = _new_annotation_type(element.__class__, cls)
return cls(element, values)
_annotations: util.immutabledict[str, Any]
__element: SupportsWrappingAnnotations
_hash: int
def __new__(cls: Type[Self], *args: Any) -> Self:
return object.__new__(cls)
def __init__(
self, element: SupportsWrappingAnnotations, values: _AnnotationDict
):
self.__dict__ = element.__dict__.copy()
self.__dict__.pop("_annotations_cache_key", None)
self.__dict__.pop("_generate_cache_key", None)
self.__element = element
self._annotations = util.immutabledict(values)
self._hash = hash(element)
def _annotate(self, values: _AnnotationDict) -> Self:
_values = self._annotations.union(values)
new = self._with_annotations(_values)
return new
def _with_annotations(self, values: _AnnotationDict) -> Self:
clone = self.__class__.__new__(self.__class__)
clone.__dict__ = self.__dict__.copy()
clone.__dict__.pop("_annotations_cache_key", None)
clone.__dict__.pop("_generate_cache_key", None)
clone._annotations = util.immutabledict(values)
return clone
@overload
def _deannotate(
self,
values: Literal[None] = ...,
clone: bool = ...,
) -> Self: ...
@overload
def _deannotate(
self,
values: Sequence[str] = ...,
clone: bool = ...,
) -> Annotated: ...
def _deannotate(
self,
values: Optional[Sequence[str]] = None,
clone: bool = True,
) -> SupportsAnnotations:
if values is None:
return self.__element
else:
return self._with_annotations(
util.immutabledict(
{
key: value
for key, value in self._annotations.items()
if key not in values
}
)
)
if not typing.TYPE_CHECKING:
# manually proxy some methods that need extra attention
def _compiler_dispatch(self, visitor: Any, **kw: Any) -> Any:
return self.__element.__class__._compiler_dispatch(
self, visitor, **kw
)
@property
def _constructor(self):
return self.__element._constructor
def _clone(self, **kw: Any) -> Self:
clone = self.__element._clone(**kw)
if clone is self.__element:
# detect immutable, don't change anything
return self
else:
# update the clone with any changes that have occurred
# to this object's __dict__.
clone.__dict__.update(self.__dict__)
return self.__class__(clone, self._annotations)
def __reduce__(self) -> Tuple[Type[Annotated], Tuple[Any, ...]]:
return self.__class__, (self.__element, self._annotations)
def __hash__(self) -> int:
return self._hash
def __eq__(self, other: Any) -> bool:
if self._is_column_operators:
return self.__element.__class__.__eq__(self, other)
else:
return hash(other) == hash(self)
@util.ro_non_memoized_property
def entity_namespace(self) -> _EntityNamespace:
if "entity_namespace" in self._annotations:
return cast(
SupportsWrappingAnnotations,
self._annotations["entity_namespace"],
).entity_namespace
else:
return self.__element.entity_namespace
# hard-generate Annotated subclasses. this technique
# is used instead of on-the-fly types (i.e. type.__new__())
# so that the resulting objects are pickleable; additionally, other
# decisions can be made up front about the type of object being annotated
# just once per class rather than per-instance.
annotated_classes: Dict[Type[SupportsWrappingAnnotations], Type[Annotated]] = (
{}
)
_SA = TypeVar("_SA", bound="SupportsAnnotations")
def _safe_annotate(to_annotate: _SA, annotations: _AnnotationDict) -> _SA:
try:
_annotate = to_annotate._annotate
except AttributeError:
# skip objects that don't actually have an `_annotate`
# attribute, namely QueryableAttribute inside of a join
# condition
return to_annotate
else:
return _annotate(annotations)
def _deep_annotate(
element: _SA,
annotations: _AnnotationDict,
exclude: Optional[Sequence[SupportsAnnotations]] = None,
*,
detect_subquery_cols: bool = False,
ind_cols_on_fromclause: bool = False,
annotate_callable: Optional[
Callable[[SupportsAnnotations, _AnnotationDict], SupportsAnnotations]
] = None,
) -> _SA:
"""Deep copy the given ClauseElement, annotating each element
with the given annotations dictionary.
Elements within the exclude collection will be cloned but not annotated.
"""
# annotated objects hack the __hash__() method so if we want to
# uniquely process them we have to use id()
cloned_ids: Dict[int, SupportsAnnotations] = {}
def clone(elem: SupportsAnnotations, **kw: Any) -> SupportsAnnotations:
# ind_cols_on_fromclause means make sure an AnnotatedFromClause
# has its own .c collection independent of that which its proxying.
# this is used specifically by orm.LoaderCriteriaOption to break
# a reference cycle that it's otherwise prone to building,
# see test_relationship_criteria->
# test_loader_criteria_subquery_w_same_entity. logic here was
# changed for #8796 and made explicit; previously it occurred
# by accident
kw["detect_subquery_cols"] = detect_subquery_cols
id_ = id(elem)
if id_ in cloned_ids:
return cloned_ids[id_]
if (
exclude
and hasattr(elem, "proxy_set")
and elem.proxy_set.intersection(exclude)
):
newelem = elem._clone(clone=clone, **kw)
elif annotations != elem._annotations:
if detect_subquery_cols and elem._is_immutable:
to_annotate = elem._clone(clone=clone, **kw)
else:
to_annotate = elem
if annotate_callable:
newelem = annotate_callable(to_annotate, annotations)
else:
newelem = _safe_annotate(to_annotate, annotations)
else:
newelem = elem
newelem._copy_internals(
clone=clone, ind_cols_on_fromclause=ind_cols_on_fromclause
)
cloned_ids[id_] = newelem
return newelem
if element is not None:
element = cast(_SA, clone(element))
clone = None # type: ignore # remove gc cycles
return element
@overload
def _deep_deannotate(
element: Literal[None], values: Optional[Sequence[str]] = None
) -> Literal[None]: ...
@overload
def _deep_deannotate(
element: _SA, values: Optional[Sequence[str]] = None
) -> _SA: ...
def _deep_deannotate(
element: Optional[_SA], values: Optional[Sequence[str]] = None
) -> Optional[_SA]:
"""Deep copy the given element, removing annotations."""
cloned: Dict[Any, SupportsAnnotations] = {}
def clone(elem: SupportsAnnotations, **kw: Any) -> SupportsAnnotations:
key: Any
if values:
key = id(elem)
else:
key = elem
if key not in cloned:
newelem = elem._deannotate(values=values, clone=True)
newelem._copy_internals(clone=clone)
cloned[key] = newelem
return newelem
else:
return cloned[key]
if element is not None:
element = cast(_SA, clone(element))
clone = None # type: ignore # remove gc cycles
return element
def _shallow_annotate(element: _SA, annotations: _AnnotationDict) -> _SA:
"""Annotate the given ClauseElement and copy its internals so that
internal objects refer to the new annotated object.
Basically used to apply a "don't traverse" annotation to a
selectable, without digging throughout the whole
structure wasting time.
"""
element = element._annotate(annotations)
element._copy_internals()
return element
def _new_annotation_type(
cls: Type[SupportsWrappingAnnotations], base_cls: Type[Annotated]
) -> Type[Annotated]:
"""Generates a new class that subclasses Annotated and proxies a given
element type.
"""
if issubclass(cls, Annotated):
return cls
elif cls in annotated_classes:
return annotated_classes[cls]
for super_ in cls.__mro__:
# check if an Annotated subclass more specific than
# the given base_cls is already registered, such
# as AnnotatedColumnElement.
if super_ in annotated_classes:
base_cls = annotated_classes[super_]
break
annotated_classes[cls] = anno_cls = cast(
Type[Annotated],
type("Annotated%s" % cls.__name__, (base_cls, cls), {}),
)
globals()["Annotated%s" % cls.__name__] = anno_cls
if "_traverse_internals" in cls.__dict__:
anno_cls._traverse_internals = list(cls._traverse_internals) + [
("_annotations", InternalTraversal.dp_annotations_key)
]
elif cls.__dict__.get("inherit_cache", False):
anno_cls._traverse_internals = list(cls._traverse_internals) + [
("_annotations", InternalTraversal.dp_annotations_key)
]
# some classes include this even if they have traverse_internals
# e.g. BindParameter, add it if present.
if cls.__dict__.get("inherit_cache", False):
anno_cls.inherit_cache = True # type: ignore
elif "inherit_cache" in cls.__dict__:
anno_cls.inherit_cache = cls.__dict__["inherit_cache"] # type: ignore
anno_cls._is_column_operators = issubclass(cls, operators.ColumnOperators)
return anno_cls
def _prepare_annotations(
target_hierarchy: Type[SupportsWrappingAnnotations],
base_cls: Type[Annotated],
) -> None:
for cls in util.walk_subclasses(target_hierarchy):
_new_annotation_type(cls, base_cls)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,552 @@
# sql/default_comparator.py
# Copyright (C) 2005-2025 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
"""Default implementation of SQL comparison operations.
"""
from __future__ import annotations
import typing
from typing import Any
from typing import Callable
from typing import Dict
from typing import NoReturn
from typing import Optional
from typing import Tuple
from typing import Type
from typing import Union
from . import coercions
from . import operators
from . import roles
from . import type_api
from .elements import and_
from .elements import BinaryExpression
from .elements import ClauseElement
from .elements import CollationClause
from .elements import CollectionAggregate
from .elements import ExpressionClauseList
from .elements import False_
from .elements import Null
from .elements import OperatorExpression
from .elements import or_
from .elements import True_
from .elements import UnaryExpression
from .operators import OperatorType
from .. import exc
from .. import util
_T = typing.TypeVar("_T", bound=Any)
if typing.TYPE_CHECKING:
from .elements import ColumnElement
from .operators import custom_op
from .type_api import TypeEngine
def _boolean_compare(
expr: ColumnElement[Any],
op: OperatorType,
obj: Any,
*,
negate_op: Optional[OperatorType] = None,
reverse: bool = False,
_python_is_types: Tuple[Type[Any], ...] = (type(None), bool),
result_type: Optional[TypeEngine[bool]] = None,
**kwargs: Any,
) -> OperatorExpression[bool]:
if result_type is None:
result_type = type_api.BOOLEANTYPE
if isinstance(obj, _python_is_types + (Null, True_, False_)):
# allow x ==/!= True/False to be treated as a literal.
# this comes out to "== / != true/false" or "1/0" if those
# constants aren't supported and works on all platforms
if op in (operators.eq, operators.ne) and isinstance(
obj, (bool, True_, False_)
):
return OperatorExpression._construct_for_op(
expr,
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
negate=negate_op,
modifiers=kwargs,
)
elif op in (
operators.is_distinct_from,
operators.is_not_distinct_from,
):
return OperatorExpression._construct_for_op(
expr,
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
negate=negate_op,
modifiers=kwargs,
)
elif expr._is_collection_aggregate:
obj = coercions.expect(
roles.ConstExprRole, element=obj, operator=op, expr=expr
)
else:
# all other None uses IS, IS NOT
if op in (operators.eq, operators.is_):
return OperatorExpression._construct_for_op(
expr,
coercions.expect(roles.ConstExprRole, obj),
operators.is_,
negate=operators.is_not,
type_=result_type,
)
elif op in (operators.ne, operators.is_not):
return OperatorExpression._construct_for_op(
expr,
coercions.expect(roles.ConstExprRole, obj),
operators.is_not,
negate=operators.is_,
type_=result_type,
)
else:
raise exc.ArgumentError(
"Only '=', '!=', 'is_()', 'is_not()', "
"'is_distinct_from()', 'is_not_distinct_from()' "
"operators can be used with None/True/False"
)
else:
obj = coercions.expect(
roles.BinaryElementRole, element=obj, operator=op, expr=expr
)
if reverse:
return OperatorExpression._construct_for_op(
obj,
expr,
op,
type_=result_type,
negate=negate_op,
modifiers=kwargs,
)
else:
return OperatorExpression._construct_for_op(
expr,
obj,
op,
type_=result_type,
negate=negate_op,
modifiers=kwargs,
)
def _custom_op_operate(
expr: ColumnElement[Any],
op: custom_op[Any],
obj: Any,
reverse: bool = False,
result_type: Optional[TypeEngine[Any]] = None,
**kw: Any,
) -> ColumnElement[Any]:
if result_type is None:
if op.return_type:
result_type = op.return_type
elif op.is_comparison:
result_type = type_api.BOOLEANTYPE
return _binary_operate(
expr, op, obj, reverse=reverse, result_type=result_type, **kw
)
def _binary_operate(
expr: ColumnElement[Any],
op: OperatorType,
obj: roles.BinaryElementRole[Any],
*,
reverse: bool = False,
result_type: Optional[TypeEngine[_T]] = None,
**kw: Any,
) -> OperatorExpression[_T]:
coerced_obj = coercions.expect(
roles.BinaryElementRole, obj, expr=expr, operator=op
)
if reverse:
left, right = coerced_obj, expr
else:
left, right = expr, coerced_obj
if result_type is None:
op, result_type = left.comparator._adapt_expression(
op, right.comparator
)
return OperatorExpression._construct_for_op(
left, right, op, type_=result_type, modifiers=kw
)
def _conjunction_operate(
expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
) -> ColumnElement[Any]:
if op is operators.and_:
return and_(expr, other)
elif op is operators.or_:
return or_(expr, other)
else:
raise NotImplementedError()
def _scalar(
expr: ColumnElement[Any],
op: OperatorType,
fn: Callable[[ColumnElement[Any]], ColumnElement[Any]],
**kw: Any,
) -> ColumnElement[Any]:
return fn(expr)
def _in_impl(
expr: ColumnElement[Any],
op: OperatorType,
seq_or_selectable: ClauseElement,
negate_op: OperatorType,
**kw: Any,
) -> ColumnElement[Any]:
seq_or_selectable = coercions.expect(
roles.InElementRole, seq_or_selectable, expr=expr, operator=op
)
if "in_ops" in seq_or_selectable._annotations:
op, negate_op = seq_or_selectable._annotations["in_ops"]
return _boolean_compare(
expr, op, seq_or_selectable, negate_op=negate_op, **kw
)
def _getitem_impl(
expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
) -> ColumnElement[Any]:
if (
isinstance(expr.type, type_api.INDEXABLE)
or isinstance(expr.type, type_api.TypeDecorator)
and isinstance(expr.type.impl_instance, type_api.INDEXABLE)
):
other = coercions.expect(
roles.BinaryElementRole, other, expr=expr, operator=op
)
return _binary_operate(expr, op, other, **kw)
else:
_unsupported_impl(expr, op, other, **kw)
def _unsupported_impl(
expr: ColumnElement[Any], op: OperatorType, *arg: Any, **kw: Any
) -> NoReturn:
raise NotImplementedError(
"Operator '%s' is not supported on this expression" % op.__name__
)
def _inv_impl(
expr: ColumnElement[Any], op: OperatorType, **kw: Any
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.__inv__`."""
# undocumented element currently used by the ORM for
# relationship.contains()
if hasattr(expr, "negation_clause"):
return expr.negation_clause
else:
return expr._negate()
def _neg_impl(
expr: ColumnElement[Any], op: OperatorType, **kw: Any
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.__neg__`."""
return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
def _bitwise_not_impl(
expr: ColumnElement[Any], op: OperatorType, **kw: Any
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.bitwise_not`."""
return UnaryExpression(
expr, operator=operators.bitwise_not_op, type_=expr.type
)
def _match_impl(
expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.match`."""
return _boolean_compare(
expr,
operators.match_op,
coercions.expect(
roles.BinaryElementRole,
other,
expr=expr,
operator=operators.match_op,
),
result_type=type_api.MATCHTYPE,
negate_op=(
operators.not_match_op
if op is operators.match_op
else operators.match_op
),
**kw,
)
def _distinct_impl(
expr: ColumnElement[Any], op: OperatorType, **kw: Any
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.distinct`."""
return UnaryExpression(
expr, operator=operators.distinct_op, type_=expr.type
)
def _between_impl(
expr: ColumnElement[Any],
op: OperatorType,
cleft: Any,
cright: Any,
**kw: Any,
) -> ColumnElement[Any]:
"""See :meth:`.ColumnOperators.between`."""
return BinaryExpression(
expr,
ExpressionClauseList._construct_for_list(
operators.and_,
type_api.NULLTYPE,
coercions.expect(
roles.BinaryElementRole,
cleft,
expr=expr,
operator=operators.and_,
),
coercions.expect(
roles.BinaryElementRole,
cright,
expr=expr,
operator=operators.and_,
),
group=False,
),
op,
negate=(
operators.not_between_op
if op is operators.between_op
else operators.between_op
),
modifiers=kw,
)
def _collate_impl(
expr: ColumnElement[str], op: OperatorType, collation: str, **kw: Any
) -> ColumnElement[str]:
return CollationClause._create_collation_expression(expr, collation)
def _regexp_match_impl(
expr: ColumnElement[str],
op: OperatorType,
pattern: Any,
flags: Optional[str],
**kw: Any,
) -> ColumnElement[Any]:
return BinaryExpression(
expr,
coercions.expect(
roles.BinaryElementRole,
pattern,
expr=expr,
operator=operators.comma_op,
),
op,
negate=operators.not_regexp_match_op,
modifiers={"flags": flags},
)
def _regexp_replace_impl(
expr: ColumnElement[Any],
op: OperatorType,
pattern: Any,
replacement: Any,
flags: Optional[str],
**kw: Any,
) -> ColumnElement[Any]:
return BinaryExpression(
expr,
ExpressionClauseList._construct_for_list(
operators.comma_op,
type_api.NULLTYPE,
coercions.expect(
roles.BinaryElementRole,
pattern,
expr=expr,
operator=operators.comma_op,
),
coercions.expect(
roles.BinaryElementRole,
replacement,
expr=expr,
operator=operators.comma_op,
),
group=False,
),
op,
modifiers={"flags": flags},
)
# a mapping of operators with the method they use, along with
# additional keyword arguments to be passed
operator_lookup: Dict[
str,
Tuple[
Callable[..., ColumnElement[Any]],
util.immutabledict[
str, Union[OperatorType, Callable[..., ColumnElement[Any]]]
],
],
] = {
"and_": (_conjunction_operate, util.EMPTY_DICT),
"or_": (_conjunction_operate, util.EMPTY_DICT),
"inv": (_inv_impl, util.EMPTY_DICT),
"add": (_binary_operate, util.EMPTY_DICT),
"mul": (_binary_operate, util.EMPTY_DICT),
"sub": (_binary_operate, util.EMPTY_DICT),
"div": (_binary_operate, util.EMPTY_DICT),
"mod": (_binary_operate, util.EMPTY_DICT),
"bitwise_xor_op": (_binary_operate, util.EMPTY_DICT),
"bitwise_or_op": (_binary_operate, util.EMPTY_DICT),
"bitwise_and_op": (_binary_operate, util.EMPTY_DICT),
"bitwise_not_op": (_bitwise_not_impl, util.EMPTY_DICT),
"bitwise_lshift_op": (_binary_operate, util.EMPTY_DICT),
"bitwise_rshift_op": (_binary_operate, util.EMPTY_DICT),
"truediv": (_binary_operate, util.EMPTY_DICT),
"floordiv": (_binary_operate, util.EMPTY_DICT),
"custom_op": (_custom_op_operate, util.EMPTY_DICT),
"json_path_getitem_op": (_binary_operate, util.EMPTY_DICT),
"json_getitem_op": (_binary_operate, util.EMPTY_DICT),
"concat_op": (_binary_operate, util.EMPTY_DICT),
"any_op": (
_scalar,
util.immutabledict({"fn": CollectionAggregate._create_any}),
),
"all_op": (
_scalar,
util.immutabledict({"fn": CollectionAggregate._create_all}),
),
"lt": (_boolean_compare, util.immutabledict({"negate_op": operators.ge})),
"le": (_boolean_compare, util.immutabledict({"negate_op": operators.gt})),
"ne": (_boolean_compare, util.immutabledict({"negate_op": operators.eq})),
"gt": (_boolean_compare, util.immutabledict({"negate_op": operators.le})),
"ge": (_boolean_compare, util.immutabledict({"negate_op": operators.lt})),
"eq": (_boolean_compare, util.immutabledict({"negate_op": operators.ne})),
"is_distinct_from": (
_boolean_compare,
util.immutabledict({"negate_op": operators.is_not_distinct_from}),
),
"is_not_distinct_from": (
_boolean_compare,
util.immutabledict({"negate_op": operators.is_distinct_from}),
),
"like_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_like_op}),
),
"ilike_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_ilike_op}),
),
"not_like_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.like_op}),
),
"not_ilike_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.ilike_op}),
),
"contains_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_contains_op}),
),
"icontains_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_icontains_op}),
),
"startswith_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_startswith_op}),
),
"istartswith_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_istartswith_op}),
),
"endswith_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_endswith_op}),
),
"iendswith_op": (
_boolean_compare,
util.immutabledict({"negate_op": operators.not_iendswith_op}),
),
"desc_op": (
_scalar,
util.immutabledict({"fn": UnaryExpression._create_desc}),
),
"asc_op": (
_scalar,
util.immutabledict({"fn": UnaryExpression._create_asc}),
),
"nulls_first_op": (
_scalar,
util.immutabledict({"fn": UnaryExpression._create_nulls_first}),
),
"nulls_last_op": (
_scalar,
util.immutabledict({"fn": UnaryExpression._create_nulls_last}),
),
"in_op": (
_in_impl,
util.immutabledict({"negate_op": operators.not_in_op}),
),
"not_in_op": (
_in_impl,
util.immutabledict({"negate_op": operators.in_op}),
),
"is_": (
_boolean_compare,
util.immutabledict({"negate_op": operators.is_}),
),
"is_not": (
_boolean_compare,
util.immutabledict({"negate_op": operators.is_not}),
),
"collate": (_collate_impl, util.EMPTY_DICT),
"match_op": (_match_impl, util.EMPTY_DICT),
"not_match_op": (_match_impl, util.EMPTY_DICT),
"distinct_op": (_distinct_impl, util.EMPTY_DICT),
"between_op": (_between_impl, util.EMPTY_DICT),
"not_between_op": (_between_impl, util.EMPTY_DICT),
"neg": (_neg_impl, util.EMPTY_DICT),
"getitem": (_getitem_impl, util.EMPTY_DICT),
"lshift": (_unsupported_impl, util.EMPTY_DICT),
"rshift": (_unsupported_impl, util.EMPTY_DICT),
"contains": (_unsupported_impl, util.EMPTY_DICT),
"regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
"not_regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
"regexp_replace_op": (_regexp_replace_impl, util.EMPTY_DICT),
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,458 @@
# sql/events.py
# Copyright (C) 2005-2025 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
from __future__ import annotations
from typing import Any
from typing import TYPE_CHECKING
from .base import SchemaEventTarget
from .. import event
if TYPE_CHECKING:
from .schema import Column
from .schema import Constraint
from .schema import SchemaItem
from .schema import Table
from ..engine.base import Connection
from ..engine.interfaces import ReflectedColumn
from ..engine.reflection import Inspector
class DDLEvents(event.Events[SchemaEventTarget]):
"""
Define event listeners for schema objects,
that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`,
:class:`_schema.Column`, etc.
**Create / Drop Events**
Events emitted when CREATE and DROP commands are emitted to the database.
The event hooks in this category include :meth:`.DDLEvents.before_create`,
:meth:`.DDLEvents.after_create`, :meth:`.DDLEvents.before_drop`, and
:meth:`.DDLEvents.after_drop`.
These events are emitted when using schema-level methods such as
:meth:`.MetaData.create_all` and :meth:`.MetaData.drop_all`. Per-object
create/drop methods such as :meth:`.Table.create`, :meth:`.Table.drop`,
:meth:`.Index.create` are also included, as well as dialect-specific
methods such as :meth:`_postgresql.ENUM.create`.
.. versionadded:: 2.0 :class:`.DDLEvents` event hooks now take place
for non-table objects including constraints, indexes, and
dialect-specific schema types.
Event hooks may be attached directly to a :class:`_schema.Table` object or
to a :class:`_schema.MetaData` collection, as well as to any
:class:`.SchemaItem` class or object that can be individually created and
dropped using a distinct SQL command. Such classes include :class:`.Index`,
:class:`.Sequence`, and dialect-specific classes such as
:class:`_postgresql.ENUM`.
Example using the :meth:`.DDLEvents.after_create` event, where a custom
event hook will emit an ``ALTER TABLE`` command on the current connection,
after ``CREATE TABLE`` is emitted::
from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy import Table, Column, Metadata, Integer
m = MetaData()
some_table = Table("some_table", m, Column("data", Integer))
@event.listens_for(some_table, "after_create")
def after_create(target, connection, **kw):
connection.execute(
text("ALTER TABLE %s SET name=foo_%s" % (target.name, target.name))
)
some_engine = create_engine("postgresql://scott:tiger@host/test")
# will emit "CREATE TABLE some_table" as well as the above
# "ALTER TABLE" statement afterwards
m.create_all(some_engine)
Constraint objects such as :class:`.ForeignKeyConstraint`,
:class:`.UniqueConstraint`, :class:`.CheckConstraint` may also be
subscribed to these events, however they will **not** normally produce
events as these objects are usually rendered inline within an
enclosing ``CREATE TABLE`` statement and implicitly dropped from a
``DROP TABLE`` statement.
For the :class:`.Index` construct, the event hook will be emitted
for ``CREATE INDEX``, however SQLAlchemy does not normally emit
``DROP INDEX`` when dropping tables as this is again implicit within the
``DROP TABLE`` statement.
.. versionadded:: 2.0 Support for :class:`.SchemaItem` objects
for create/drop events was expanded from its previous support for
:class:`.MetaData` and :class:`.Table` to also include
:class:`.Constraint` and all subclasses, :class:`.Index`,
:class:`.Sequence` and some type-related constructs such as
:class:`_postgresql.ENUM`.
.. note:: These event hooks are only emitted within the scope of
SQLAlchemy's create/drop methods; they are not necessarily supported
by tools such as `alembic <https://alembic.sqlalchemy.org>`_.
**Attachment Events**
Attachment events are provided to customize
behavior whenever a child schema element is associated
with a parent, such as when a :class:`_schema.Column` is associated
with its :class:`_schema.Table`, when a
:class:`_schema.ForeignKeyConstraint`
is associated with a :class:`_schema.Table`, etc. These events include
:meth:`.DDLEvents.before_parent_attach` and
:meth:`.DDLEvents.after_parent_attach`.
**Reflection Events**
The :meth:`.DDLEvents.column_reflect` event is used to intercept
and modify the in-Python definition of database columns when
:term:`reflection` of database tables proceeds.
**Use with Generic DDL**
DDL events integrate closely with the
:class:`.DDL` class and the :class:`.ExecutableDDLElement` hierarchy
of DDL clause constructs, which are themselves appropriate
as listener callables::
from sqlalchemy import DDL
event.listen(
some_table,
"after_create",
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
)
**Event Propagation to MetaData Copies**
For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
will ensure that a given event handler is propagated to copies of the
object, which are made when using the :meth:`_schema.Table.to_metadata`
method::
from sqlalchemy import DDL
metadata = MetaData()
some_table = Table("some_table", metadata, Column("data", Integer))
event.listen(
some_table,
"after_create",
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
propagate=True,
)
new_metadata = MetaData()
new_table = some_table.to_metadata(new_metadata)
The above :class:`.DDL` object will be associated with the
:meth:`.DDLEvents.after_create` event for both the ``some_table`` and
the ``new_table`` :class:`.Table` objects.
.. seealso::
:ref:`event_toplevel`
:class:`.ExecutableDDLElement`
:class:`.DDL`
:ref:`schema_ddl_sequences`
""" # noqa: E501
_target_class_doc = "SomeSchemaClassOrObject"
_dispatch_target = SchemaEventTarget
def before_create(
self, target: SchemaEventTarget, connection: Connection, **kw: Any
) -> None:
r"""Called before CREATE statements are emitted.
:param target: the :class:`.SchemaObject`, such as a
:class:`_schema.MetaData` or :class:`_schema.Table`
but also including all create/drop objects such as
:class:`.Index`, :class:`.Sequence`, etc.,
object which is the target of the event.
.. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
was added.
:param connection: the :class:`_engine.Connection` where the
CREATE statement or statements will be emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
:func:`.event.listen` accepts the ``insert=True``
modifier for this event; when True, the listener function will
be prepended to the internal list of events upon discovery, and execute
before registered listener functions that do not pass this argument.
"""
def after_create(
self, target: SchemaEventTarget, connection: Connection, **kw: Any
) -> None:
r"""Called after CREATE statements are emitted.
:param target: the :class:`.SchemaObject`, such as a
:class:`_schema.MetaData` or :class:`_schema.Table`
but also including all create/drop objects such as
:class:`.Index`, :class:`.Sequence`, etc.,
object which is the target of the event.
.. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
was added.
:param connection: the :class:`_engine.Connection` where the
CREATE statement or statements have been emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def before_drop(
self, target: SchemaEventTarget, connection: Connection, **kw: Any
) -> None:
r"""Called before DROP statements are emitted.
:param target: the :class:`.SchemaObject`, such as a
:class:`_schema.MetaData` or :class:`_schema.Table`
but also including all create/drop objects such as
:class:`.Index`, :class:`.Sequence`, etc.,
object which is the target of the event.
.. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
was added.
:param connection: the :class:`_engine.Connection` where the
DROP statement or statements will be emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def after_drop(
self, target: SchemaEventTarget, connection: Connection, **kw: Any
) -> None:
r"""Called after DROP statements are emitted.
:param target: the :class:`.SchemaObject`, such as a
:class:`_schema.MetaData` or :class:`_schema.Table`
but also including all create/drop objects such as
:class:`.Index`, :class:`.Sequence`, etc.,
object which is the target of the event.
.. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects
was added.
:param connection: the :class:`_engine.Connection` where the
DROP statement or statements have been emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def before_parent_attach(
self, target: SchemaEventTarget, parent: SchemaItem
) -> None:
"""Called before a :class:`.SchemaItem` is associated with
a parent :class:`.SchemaItem`.
:param target: the target object
:param parent: the parent to which the target is being attached.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def after_parent_attach(
self, target: SchemaEventTarget, parent: SchemaItem
) -> None:
"""Called after a :class:`.SchemaItem` is associated with
a parent :class:`.SchemaItem`.
:param target: the target object
:param parent: the parent to which the target is being attached.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def _sa_event_column_added_to_pk_constraint(
self, const: Constraint, col: Column[Any]
) -> None:
"""internal event hook used for primary key naming convention
updates.
"""
def column_reflect(
self, inspector: Inspector, table: Table, column_info: ReflectedColumn
) -> None:
"""Called for each unit of 'column info' retrieved when
a :class:`_schema.Table` is being reflected.
This event is most easily used by applying it to a specific
:class:`_schema.MetaData` instance, where it will take effect for
all :class:`_schema.Table` objects within that
:class:`_schema.MetaData` that undergo reflection::
metadata = MetaData()
@event.listens_for(metadata, "column_reflect")
def receive_column_reflect(inspector, table, column_info):
# receives for all Table objects that are reflected
# under this MetaData
...
# will use the above event hook
my_table = Table("my_table", metadata, autoload_with=some_engine)
.. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect`
hook may now be applied to a :class:`_schema.MetaData` object as
well as the :class:`_schema.MetaData` class itself where it will
take place for all :class:`_schema.Table` objects associated with
the targeted :class:`_schema.MetaData`.
It may also be applied to the :class:`_schema.Table` class across
the board::
from sqlalchemy import Table
@event.listens_for(Table, "column_reflect")
def receive_column_reflect(inspector, table, column_info):
# receives for all Table objects that are reflected
...
It can also be applied to a specific :class:`_schema.Table` at the
point that one is being reflected using the
:paramref:`_schema.Table.listeners` parameter::
t1 = Table(
"my_table",
autoload_with=some_engine,
listeners=[("column_reflect", receive_column_reflect)],
)
The dictionary of column information as returned by the
dialect is passed, and can be modified. The dictionary
is that returned in each element of the list returned
by :meth:`.reflection.Inspector.get_columns`:
* ``name`` - the column's name, is applied to the
:paramref:`_schema.Column.name` parameter
* ``type`` - the type of this column, which should be an instance
of :class:`~sqlalchemy.types.TypeEngine`, is applied to the
:paramref:`_schema.Column.type` parameter
* ``nullable`` - boolean flag if the column is NULL or NOT NULL,
is applied to the :paramref:`_schema.Column.nullable` parameter
* ``default`` - the column's server default value. This is
normally specified as a plain string SQL expression, however the
event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
or :func:`_expression.text` object as well. Is applied to the
:paramref:`_schema.Column.server_default` parameter
The event is called before any action is taken against
this dictionary, and the contents can be modified; the following
additional keys may be added to the dictionary to further modify
how the :class:`_schema.Column` is constructed:
* ``key`` - the string key that will be used to access this
:class:`_schema.Column` in the ``.c`` collection; will be applied
to the :paramref:`_schema.Column.key` parameter. Is also used
for ORM mapping. See the section
:ref:`mapper_automated_reflection_schemes` for an example.
* ``quote`` - force or un-force quoting on the column name;
is applied to the :paramref:`_schema.Column.quote` parameter.
* ``info`` - a dictionary of arbitrary data to follow along with
the :class:`_schema.Column`, is applied to the
:paramref:`_schema.Column.info` parameter.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
.. seealso::
:ref:`mapper_automated_reflection_schemes` -
in the ORM mapping documentation
:ref:`automap_intercepting_columns` -
in the :ref:`automap_toplevel` documentation
:ref:`metadata_reflection_dbagnostic_types` - in
the :ref:`metadata_reflection_toplevel` documentation
"""

View File

@ -0,0 +1,162 @@
# sql/expression.py
# Copyright (C) 2005-2025 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
"""Defines the public namespace for SQL expression constructs.
"""
from __future__ import annotations
from ._dml_constructors import delete as delete
from ._dml_constructors import insert as insert
from ._dml_constructors import update as update
from ._elements_constructors import all_ as all_
from ._elements_constructors import and_ as and_
from ._elements_constructors import any_ as any_
from ._elements_constructors import asc as asc
from ._elements_constructors import between as between
from ._elements_constructors import bindparam as bindparam
from ._elements_constructors import bitwise_not as bitwise_not
from ._elements_constructors import case as case
from ._elements_constructors import cast as cast
from ._elements_constructors import collate as collate
from ._elements_constructors import column as column
from ._elements_constructors import desc as desc
from ._elements_constructors import distinct as distinct
from ._elements_constructors import extract as extract
from ._elements_constructors import false as false
from ._elements_constructors import funcfilter as funcfilter
from ._elements_constructors import label as label
from ._elements_constructors import not_ as not_
from ._elements_constructors import null as null
from ._elements_constructors import nulls_first as nulls_first
from ._elements_constructors import nulls_last as nulls_last
from ._elements_constructors import or_ as or_
from ._elements_constructors import outparam as outparam
from ._elements_constructors import over as over
from ._elements_constructors import text as text
from ._elements_constructors import true as true
from ._elements_constructors import try_cast as try_cast
from ._elements_constructors import tuple_ as tuple_
from ._elements_constructors import type_coerce as type_coerce
from ._elements_constructors import within_group as within_group
from ._selectable_constructors import alias as alias
from ._selectable_constructors import cte as cte
from ._selectable_constructors import except_ as except_
from ._selectable_constructors import except_all as except_all
from ._selectable_constructors import exists as exists
from ._selectable_constructors import intersect as intersect
from ._selectable_constructors import intersect_all as intersect_all
from ._selectable_constructors import join as join
from ._selectable_constructors import lateral as lateral
from ._selectable_constructors import outerjoin as outerjoin
from ._selectable_constructors import select as select
from ._selectable_constructors import table as table
from ._selectable_constructors import tablesample as tablesample
from ._selectable_constructors import union as union
from ._selectable_constructors import union_all as union_all
from ._selectable_constructors import values as values
from ._typing import ColumnExpressionArgument as ColumnExpressionArgument
from .base import _from_objects as _from_objects
from .base import _select_iterables as _select_iterables
from .base import ColumnCollection as ColumnCollection
from .base import Executable as Executable
from .cache_key import CacheKey as CacheKey
from .dml import Delete as Delete
from .dml import Insert as Insert
from .dml import Update as Update
from .dml import UpdateBase as UpdateBase
from .dml import ValuesBase as ValuesBase
from .elements import _truncated_label as _truncated_label
from .elements import BinaryExpression as BinaryExpression
from .elements import BindParameter as BindParameter
from .elements import BooleanClauseList as BooleanClauseList
from .elements import Case as Case
from .elements import Cast as Cast
from .elements import ClauseElement as ClauseElement
from .elements import ClauseList as ClauseList
from .elements import CollectionAggregate as CollectionAggregate
from .elements import ColumnClause as ColumnClause
from .elements import ColumnElement as ColumnElement
from .elements import ExpressionClauseList as ExpressionClauseList
from .elements import Extract as Extract
from .elements import False_ as False_
from .elements import FunctionFilter as FunctionFilter
from .elements import Grouping as Grouping
from .elements import Label as Label
from .elements import literal as literal
from .elements import literal_column as literal_column
from .elements import Null as Null
from .elements import Over as Over
from .elements import quoted_name as quoted_name
from .elements import ReleaseSavepointClause as ReleaseSavepointClause
from .elements import RollbackToSavepointClause as RollbackToSavepointClause
from .elements import SavepointClause as SavepointClause
from .elements import SQLColumnExpression as SQLColumnExpression
from .elements import TextClause as TextClause
from .elements import True_ as True_
from .elements import TryCast as TryCast
from .elements import Tuple as Tuple
from .elements import TypeClause as TypeClause
from .elements import TypeCoerce as TypeCoerce
from .elements import UnaryExpression as UnaryExpression
from .elements import WithinGroup as WithinGroup
from .functions import func as func
from .functions import Function as Function
from .functions import FunctionElement as FunctionElement
from .functions import modifier as modifier
from .lambdas import lambda_stmt as lambda_stmt
from .lambdas import LambdaElement as LambdaElement
from .lambdas import StatementLambdaElement as StatementLambdaElement
from .operators import ColumnOperators as ColumnOperators
from .operators import custom_op as custom_op
from .operators import Operators as Operators
from .selectable import Alias as Alias
from .selectable import AliasedReturnsRows as AliasedReturnsRows
from .selectable import CompoundSelect as CompoundSelect
from .selectable import CTE as CTE
from .selectable import Exists as Exists
from .selectable import FromClause as FromClause
from .selectable import FromGrouping as FromGrouping
from .selectable import GenerativeSelect as GenerativeSelect
from .selectable import HasCTE as HasCTE
from .selectable import HasPrefixes as HasPrefixes
from .selectable import HasSuffixes as HasSuffixes
from .selectable import Join as Join
from .selectable import LABEL_STYLE_DEFAULT as LABEL_STYLE_DEFAULT
from .selectable import (
LABEL_STYLE_DISAMBIGUATE_ONLY as LABEL_STYLE_DISAMBIGUATE_ONLY,
)
from .selectable import LABEL_STYLE_NONE as LABEL_STYLE_NONE
from .selectable import (
LABEL_STYLE_TABLENAME_PLUS_COL as LABEL_STYLE_TABLENAME_PLUS_COL,
)
from .selectable import Lateral as Lateral
from .selectable import ReturnsRows as ReturnsRows
from .selectable import ScalarSelect as ScalarSelect
from .selectable import ScalarValues as ScalarValues
from .selectable import Select as Select
from .selectable import Selectable as Selectable
from .selectable import SelectBase as SelectBase
from .selectable import SelectLabelStyle as SelectLabelStyle
from .selectable import Subquery as Subquery
from .selectable import TableClause as TableClause
from .selectable import TableSample as TableSample
from .selectable import TableValuedAlias as TableValuedAlias
from .selectable import TextAsFrom as TextAsFrom
from .selectable import TextualSelect as TextualSelect
from .selectable import Values as Values
from .visitors import Visitable as Visitable
nullsfirst = nulls_first
"""Synonym for the :func:`.nulls_first` function."""
nullslast = nulls_last
"""Synonym for the :func:`.nulls_last` function."""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,212 @@
# sql/naming.py
# Copyright (C) 2005-2025 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
# mypy: allow-untyped-defs, allow-untyped-calls
"""Establish constraint and index naming conventions.
"""
from __future__ import annotations
import re
from . import events # noqa
from .base import _NONE_NAME
from .elements import conv as conv
from .schema import CheckConstraint
from .schema import Column
from .schema import Constraint
from .schema import ForeignKeyConstraint
from .schema import Index
from .schema import PrimaryKeyConstraint
from .schema import Table
from .schema import UniqueConstraint
from .. import event
from .. import exc
class ConventionDict:
def __init__(self, const, table, convention):
self.const = const
self._is_fk = isinstance(const, ForeignKeyConstraint)
self.table = table
self.convention = convention
self._const_name = const.name
def _key_table_name(self):
return self.table.name
def _column_X(self, idx, attrname):
if self._is_fk:
try:
fk = self.const.elements[idx]
except IndexError:
return ""
else:
return getattr(fk.parent, attrname)
else:
cols = list(self.const.columns)
try:
col = cols[idx]
except IndexError:
return ""
else:
return getattr(col, attrname)
def _key_constraint_name(self):
if self._const_name in (None, _NONE_NAME):
raise exc.InvalidRequestError(
"Naming convention including "
"%(constraint_name)s token requires that "
"constraint is explicitly named."
)
if not isinstance(self._const_name, conv):
self.const.name = None
return self._const_name
def _key_column_X_key(self, idx):
# note this method was missing before
# [ticket:3989], meaning tokens like ``%(column_0_key)s`` weren't
# working even though documented.
return self._column_X(idx, "key")
def _key_column_X_name(self, idx):
return self._column_X(idx, "name")
def _key_column_X_label(self, idx):
return self._column_X(idx, "_ddl_label")
def _key_referred_table_name(self):
fk = self.const.elements[0]
refs = fk.target_fullname.split(".")
if len(refs) == 3:
refschema, reftable, refcol = refs
else:
reftable, refcol = refs
return reftable
def _key_referred_column_X_name(self, idx):
fk = self.const.elements[idx]
# note that before [ticket:3989], this method was returning
# the specification for the :class:`.ForeignKey` itself, which normally
# would be using the ``.key`` of the column, not the name.
return fk.column.name
def __getitem__(self, key):
if key in self.convention:
return self.convention[key](self.const, self.table)
elif hasattr(self, "_key_%s" % key):
return getattr(self, "_key_%s" % key)()
else:
col_template = re.match(r".*_?column_(\d+)(_?N)?_.+", key)
if col_template:
idx = col_template.group(1)
multiples = col_template.group(2)
if multiples:
if self._is_fk:
elems = self.const.elements
else:
elems = list(self.const.columns)
tokens = []
for idx, elem in enumerate(elems):
attr = "_key_" + key.replace("0" + multiples, "X")
try:
tokens.append(getattr(self, attr)(idx))
except AttributeError:
raise KeyError(key)
sep = "_" if multiples.startswith("_") else ""
return sep.join(tokens)
else:
attr = "_key_" + key.replace(idx, "X")
idx = int(idx)
if hasattr(self, attr):
return getattr(self, attr)(idx)
raise KeyError(key)
_prefix_dict = {
Index: "ix",
PrimaryKeyConstraint: "pk",
CheckConstraint: "ck",
UniqueConstraint: "uq",
ForeignKeyConstraint: "fk",
}
def _get_convention(dict_, key):
for super_ in key.__mro__:
if super_ in _prefix_dict and _prefix_dict[super_] in dict_:
return dict_[_prefix_dict[super_]]
elif super_ in dict_:
return dict_[super_]
else:
return None
def _constraint_name_for_table(const, table):
metadata = table.metadata
convention = _get_convention(metadata.naming_convention, type(const))
if isinstance(const.name, conv):
return const.name
elif (
convention is not None
and not isinstance(const.name, conv)
and (
const.name is None
or "constraint_name" in convention
or const.name is _NONE_NAME
)
):
return conv(
convention
% ConventionDict(const, table, metadata.naming_convention)
)
elif convention is _NONE_NAME:
return None
@event.listens_for(
PrimaryKeyConstraint, "_sa_event_column_added_to_pk_constraint"
)
def _column_added_to_pk_constraint(pk_constraint, col):
if pk_constraint._implicit_generated:
# only operate upon the "implicit" pk constraint for now,
# as we have to force the name to None to reset it. the
# "implicit" constraint will only have a naming convention name
# if at all.
table = pk_constraint.table
pk_constraint.name = None
newname = _constraint_name_for_table(pk_constraint, table)
if newname:
pk_constraint.name = newname
@event.listens_for(Constraint, "after_parent_attach")
@event.listens_for(Index, "after_parent_attach")
def _constraint_name(const, table):
if isinstance(table, Column):
# this path occurs for a CheckConstraint linked to a Column
# for column-attached constraint, set another event
# to link the column attached to the table as this constraint
# associated with the table.
event.listen(
table,
"after_parent_attach",
lambda col, table: _constraint_name(const, table),
)
elif isinstance(table, Table):
if isinstance(const.name, conv) or const.name is _NONE_NAME:
return
newname = _constraint_name_for_table(const, table)
if newname:
const.name = newname

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,323 @@
# sql/roles.py
# Copyright (C) 2005-2025 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
from __future__ import annotations
from typing import Any
from typing import Generic
from typing import Optional
from typing import TYPE_CHECKING
from typing import TypeVar
from .. import util
from ..util.typing import Literal
if TYPE_CHECKING:
from ._typing import _PropagateAttrsType
from .elements import Label
from .selectable import _SelectIterable
from .selectable import FromClause
from .selectable import Subquery
_T = TypeVar("_T", bound=Any)
_T_co = TypeVar("_T_co", bound=Any, covariant=True)
class SQLRole:
"""Define a "role" within a SQL statement structure.
Classes within SQL Core participate within SQLRole hierarchies in order
to more accurately indicate where they may be used within SQL statements
of all types.
.. versionadded:: 1.4
"""
__slots__ = ()
allows_lambda = False
uses_inspection = False
class UsesInspection:
__slots__ = ()
_post_inspect: Literal[None] = None
uses_inspection = True
class AllowsLambdaRole:
__slots__ = ()
allows_lambda = True
class HasCacheKeyRole(SQLRole):
__slots__ = ()
_role_name = "Cacheable Core or ORM object"
class ExecutableOptionRole(SQLRole):
__slots__ = ()
_role_name = "ExecutionOption Core or ORM object"
class LiteralValueRole(SQLRole):
__slots__ = ()
_role_name = "Literal Python value"
class ColumnArgumentRole(SQLRole):
__slots__ = ()
_role_name = "Column expression"
class ColumnArgumentOrKeyRole(ColumnArgumentRole):
__slots__ = ()
_role_name = "Column expression or string key"
class StrAsPlainColumnRole(ColumnArgumentRole):
__slots__ = ()
_role_name = "Column expression or string key"
class ColumnListRole(SQLRole):
"""Elements suitable for forming comma separated lists of expressions."""
__slots__ = ()
class StringRole(SQLRole):
"""mixin indicating a role that results in strings"""
__slots__ = ()
class TruncatedLabelRole(StringRole, SQLRole):
__slots__ = ()
_role_name = "String SQL identifier"
class ColumnsClauseRole(AllowsLambdaRole, UsesInspection, ColumnListRole):
__slots__ = ()
_role_name = (
"Column expression, FROM clause, or other columns clause element"
)
@property
def _select_iterable(self) -> _SelectIterable:
raise NotImplementedError()
class TypedColumnsClauseRole(Generic[_T_co], SQLRole):
"""element-typed form of ColumnsClauseRole"""
__slots__ = ()
class LimitOffsetRole(SQLRole):
__slots__ = ()
_role_name = "LIMIT / OFFSET expression"
class ByOfRole(ColumnListRole):
__slots__ = ()
_role_name = "GROUP BY / OF / etc. expression"
class GroupByRole(AllowsLambdaRole, UsesInspection, ByOfRole):
__slots__ = ()
# note there's a special case right now where you can pass a whole
# ORM entity to group_by() and it splits out. we may not want to keep
# this around
_role_name = "GROUP BY expression"
class OrderByRole(AllowsLambdaRole, ByOfRole):
__slots__ = ()
_role_name = "ORDER BY expression"
class StructuralRole(SQLRole):
__slots__ = ()
class StatementOptionRole(StructuralRole):
__slots__ = ()
_role_name = "statement sub-expression element"
class OnClauseRole(AllowsLambdaRole, StructuralRole):
__slots__ = ()
_role_name = (
"ON clause, typically a SQL expression or "
"ORM relationship attribute"
)
class WhereHavingRole(OnClauseRole):
__slots__ = ()
_role_name = "SQL expression for WHERE/HAVING role"
class ExpressionElementRole(TypedColumnsClauseRole[_T_co]):
# note when using generics for ExpressionElementRole,
# the generic type needs to be in
# sqlalchemy.sql.coercions._impl_lookup mapping also.
# these are set up for basic types like int, bool, str, float
# right now
__slots__ = ()
_role_name = "SQL expression element"
def label(self, name: Optional[str]) -> Label[_T]:
raise NotImplementedError()
class ConstExprRole(ExpressionElementRole[_T]):
__slots__ = ()
_role_name = "Constant True/False/None expression"
class LabeledColumnExprRole(ExpressionElementRole[_T]):
__slots__ = ()
class BinaryElementRole(ExpressionElementRole[_T]):
__slots__ = ()
_role_name = "SQL expression element or literal value"
class InElementRole(SQLRole):
__slots__ = ()
_role_name = (
"IN expression list, SELECT construct, or bound parameter object"
)
class JoinTargetRole(AllowsLambdaRole, UsesInspection, StructuralRole):
__slots__ = ()
_role_name = (
"Join target, typically a FROM expression, or ORM "
"relationship attribute"
)
class FromClauseRole(ColumnsClauseRole, JoinTargetRole):
__slots__ = ()
_role_name = "FROM expression, such as a Table or alias() object"
_is_subquery = False
named_with_column: bool
class StrictFromClauseRole(FromClauseRole):
__slots__ = ()
# does not allow text() or select() objects
class AnonymizedFromClauseRole(StrictFromClauseRole):
__slots__ = ()
if TYPE_CHECKING:
def _anonymous_fromclause(
self, *, name: Optional[str] = None, flat: bool = False
) -> FromClause: ...
class ReturnsRowsRole(SQLRole):
__slots__ = ()
_role_name = (
"Row returning expression such as a SELECT, a FROM clause, or an "
"INSERT/UPDATE/DELETE with RETURNING"
)
class StatementRole(SQLRole):
__slots__ = ()
_role_name = "Executable SQL or text() construct"
if TYPE_CHECKING:
@util.memoized_property
def _propagate_attrs(self) -> _PropagateAttrsType: ...
else:
_propagate_attrs = util.EMPTY_DICT
class SelectStatementRole(StatementRole, ReturnsRowsRole):
__slots__ = ()
_role_name = "SELECT construct or equivalent text() construct"
def subquery(self) -> Subquery:
raise NotImplementedError(
"All SelectStatementRole objects should implement a "
".subquery() method."
)
class HasCTERole(ReturnsRowsRole):
__slots__ = ()
class IsCTERole(SQLRole):
__slots__ = ()
_role_name = "CTE object"
class CompoundElementRole(AllowsLambdaRole, SQLRole):
"""SELECT statements inside a CompoundSelect, e.g. UNION, EXTRACT, etc."""
__slots__ = ()
_role_name = (
"SELECT construct for inclusion in a UNION or other set construct"
)
# TODO: are we using this?
class DMLRole(StatementRole):
__slots__ = ()
class DMLTableRole(FromClauseRole):
__slots__ = ()
_role_name = "subject table for an INSERT, UPDATE or DELETE"
class DMLColumnRole(SQLRole):
__slots__ = ()
_role_name = "SET/VALUES column expression or string key"
class DMLSelectRole(SQLRole):
"""A SELECT statement embedded in DML, typically INSERT from SELECT"""
__slots__ = ()
_role_name = "SELECT statement or equivalent textual object"
class DDLRole(StatementRole):
__slots__ = ()
class DDLExpressionRole(StructuralRole):
__slots__ = ()
_role_name = "SQL expression element for DDL constraint"
class DDLConstraintColumnRole(SQLRole):
__slots__ = ()
_role_name = "String column name or column expression for DDL constraint"
class DDLReferredColumnRole(DDLConstraintColumnRole):
__slots__ = ()
_role_name = (
"String column name or Column object for DDL foreign key constraint"
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff