diff options
Diffstat (limited to 'venv/lib/python3.11/site-packages/sqlalchemy/sql')
62 files changed, 0 insertions, 57171 deletions
diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__init__.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__init__.py deleted file mode 100644 index 9e0d2ca..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__init__.py +++ /dev/null @@ -1,145 +0,0 @@ -# sql/__init__.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -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()) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc Binary files differdeleted file mode 100644 index 16135d0..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc Binary files differdeleted file mode 100644 index 0525743..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc Binary files differdeleted file mode 100644 index 493de78..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc Binary files differdeleted file mode 100644 index 76e4a97..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc Binary files differdeleted file mode 100644 index ab0a578..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc Binary files differdeleted file mode 100644 index 29ea597..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc Binary files differdeleted file mode 100644 index d5f60fb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc Binary files differdeleted file mode 100644 index d52f144..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/base.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/base.cpython-311.pyc Binary files differdeleted file mode 100644 index efe2b16..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/base.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc Binary files differdeleted file mode 100644 index fa315ca..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc Binary files differdeleted file mode 100644 index 132dce4..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc Binary files differdeleted file mode 100644 index 1f1a5fc..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-311.pyc Binary files differdeleted file mode 100644 index 021ffb3..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc Binary files differdeleted file mode 100644 index 3f5a4e9..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc Binary files differdeleted file mode 100644 index ae92d63..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-311.pyc Binary files differdeleted file mode 100644 index 14985ca..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-311.pyc Binary files differdeleted file mode 100644 index 3eda846..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/events.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/events.cpython-311.pyc Binary files differdeleted file mode 100644 index 6ff8a38..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/events.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-311.pyc Binary files differdeleted file mode 100644 index e3d2b68..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-311.pyc Binary files differdeleted file mode 100644 index f03311a..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc Binary files differdeleted file mode 100644 index 9eea091..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-311.pyc Binary files differdeleted file mode 100644 index 86ebcd9..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-311.pyc Binary files differdeleted file mode 100644 index 36ffdc3..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-311.pyc Binary files differdeleted file mode 100644 index d3dab5d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-311.pyc Binary files differdeleted file mode 100644 index ca5ea38..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc Binary files differdeleted file mode 100644 index fea6f8f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc Binary files differdeleted file mode 100644 index 1214a08..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc Binary files differdeleted file mode 100644 index f1c5425..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc Binary files differdeleted file mode 100644 index 47f73cd..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/util.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/util.cpython-311.pyc Binary files differdeleted file mode 100644 index 1a4cda5..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/util.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc b/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc Binary files differdeleted file mode 100644 index 63c33db..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc +++ /dev/null diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_dml_constructors.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_dml_constructors.py deleted file mode 100644 index a7ead52..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_dml_constructors.py +++ /dev/null @@ -1,140 +0,0 @@ -# sql/_dml_constructors.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -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` - - """ - 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` - - - """ - 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) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_elements_constructors.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_elements_constructors.py deleted file mode 100644 index 77cc2a8..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_elements_constructors.py +++ /dev/null @@ -1,1840 +0,0 @@ -# sql/_elements_constructors.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -from __future__ import annotations - -import typing -from typing import Any -from typing import Callable -from typing import Mapping -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Tuple as typing_Tuple -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import coercions -from . import roles -from .base import _NoArg -from .coercions import _document_text_coercion -from .elements import BindParameter -from .elements import BooleanClauseList -from .elements import Case -from .elements import Cast -from .elements import CollationClause -from .elements import CollectionAggregate -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import Extract -from .elements import False_ -from .elements import FunctionFilter -from .elements import Label -from .elements import Null -from .elements import Over -from .elements import TextClause -from .elements import True_ -from .elements import TryCast -from .elements import Tuple -from .elements import TypeCoerce -from .elements import UnaryExpression -from .elements import WithinGroup -from .functions import FunctionElement -from ..util.typing import Literal - -if typing.TYPE_CHECKING: - from ._typing import _ByArgument - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnExpressionOrLiteralArgument - from ._typing import _ColumnExpressionOrStrLabelArgument - from ._typing import _TypeEngineArgument - from .elements import BinaryExpression - from .selectable import FromClause - from .type_api import TypeEngine - -_T = TypeVar("_T") - - -def all_(expr: _ColumnExpressionArgument[_T]) -> CollectionAggregate[bool]: - """Produce an ALL expression. - - For dialects such as that of PostgreSQL, this operator applies - to usage of the :class:`_types.ARRAY` datatype, for that of - MySQL, it may apply to a subquery. e.g.:: - - # renders on PostgreSQL: - # '5 = ALL (somearray)' - expr = 5 == all_(mytable.c.somearray) - - # renders on MySQL: - # '5 = ALL (SELECT value FROM table)' - expr = 5 == all_(select(table.c.value)) - - Comparison to NULL may work using ``None``:: - - None == all_(mytable.c.somearray) - - The any_() / all_() operators also feature a special "operand flipping" - behavior such that if any_() / all_() are used on the left side of a - comparison using a standalone operator such as ``==``, ``!=``, etc. - (not including operator methods such as - :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped:: - - # would render '5 = ALL (column)` - all_(mytable.c.column) == 5 - - Or with ``None``, which note will not perform - the usual step of rendering "IS" as is normally the case for NULL:: - - # would render 'NULL = ALL(somearray)' - all_(mytable.c.somearray) == None - - .. versionchanged:: 1.4.26 repaired the use of any_() / all_() - comparing to NULL on the right side to be flipped to the left. - - The column-level :meth:`_sql.ColumnElement.all_` method (not to be - confused with :class:`_types.ARRAY` level - :meth:`_types.ARRAY.Comparator.all`) is shorthand for - ``all_(col)``:: - - 5 == mytable.c.somearray.all_() - - .. seealso:: - - :meth:`_sql.ColumnOperators.all_` - - :func:`_expression.any_` - - """ - return CollectionAggregate._create_all(expr) - - -def and_( # type: ignore[empty-body] - initial_clause: Union[Literal[True], _ColumnExpressionArgument[bool]], - *clauses: _ColumnExpressionArgument[bool], -) -> ColumnElement[bool]: - r"""Produce a conjunction of expressions joined by ``AND``. - - E.g.:: - - from sqlalchemy import and_ - - stmt = select(users_table).where( - and_( - users_table.c.name == 'wendy', - users_table.c.enrolled == True - ) - ) - - The :func:`.and_` conjunction is also available using the - Python ``&`` operator (though note that compound expressions - need to be parenthesized in order to function with Python - operator precedence behavior):: - - stmt = select(users_table).where( - (users_table.c.name == 'wendy') & - (users_table.c.enrolled == True) - ) - - The :func:`.and_` operation is also implicit in some cases; - the :meth:`_expression.Select.where` - method for example can be invoked multiple - times against a statement, which will have the effect of each - clause being combined using :func:`.and_`:: - - stmt = select(users_table).\ - where(users_table.c.name == 'wendy').\ - where(users_table.c.enrolled == True) - - The :func:`.and_` construct must be given at least one positional - argument in order to be valid; a :func:`.and_` construct with no - arguments is ambiguous. To produce an "empty" or dynamically - generated :func:`.and_` expression, from a given list of expressions, - a "default" element of :func:`_sql.true` (or just ``True``) should be - specified:: - - from sqlalchemy import true - criteria = and_(true(), *expressions) - - The above expression will compile to SQL as the expression ``true`` - or ``1 = 1``, depending on backend, if no other expressions are - present. If expressions are present, then the :func:`_sql.true` value is - ignored as it does not affect the outcome of an AND expression that - has other elements. - - .. deprecated:: 1.4 The :func:`.and_` element now requires that at - least one argument is passed; creating the :func:`.and_` construct - with no arguments is deprecated, and will emit a deprecation warning - while continuing to produce a blank SQL string. - - .. seealso:: - - :func:`.or_` - - """ - ... - - -if not TYPE_CHECKING: - # handle deprecated case which allows zero-arguments - def and_(*clauses): # noqa: F811 - r"""Produce a conjunction of expressions joined by ``AND``. - - E.g.:: - - from sqlalchemy import and_ - - stmt = select(users_table).where( - and_( - users_table.c.name == 'wendy', - users_table.c.enrolled == True - ) - ) - - The :func:`.and_` conjunction is also available using the - Python ``&`` operator (though note that compound expressions - need to be parenthesized in order to function with Python - operator precedence behavior):: - - stmt = select(users_table).where( - (users_table.c.name == 'wendy') & - (users_table.c.enrolled == True) - ) - - The :func:`.and_` operation is also implicit in some cases; - the :meth:`_expression.Select.where` - method for example can be invoked multiple - times against a statement, which will have the effect of each - clause being combined using :func:`.and_`:: - - stmt = select(users_table).\ - where(users_table.c.name == 'wendy').\ - where(users_table.c.enrolled == True) - - The :func:`.and_` construct must be given at least one positional - argument in order to be valid; a :func:`.and_` construct with no - arguments is ambiguous. To produce an "empty" or dynamically - generated :func:`.and_` expression, from a given list of expressions, - a "default" element of :func:`_sql.true` (or just ``True``) should be - specified:: - - from sqlalchemy import true - criteria = and_(true(), *expressions) - - The above expression will compile to SQL as the expression ``true`` - or ``1 = 1``, depending on backend, if no other expressions are - present. If expressions are present, then the :func:`_sql.true` value - is ignored as it does not affect the outcome of an AND expression that - has other elements. - - .. deprecated:: 1.4 The :func:`.and_` element now requires that at - least one argument is passed; creating the :func:`.and_` construct - with no arguments is deprecated, and will emit a deprecation warning - while continuing to produce a blank SQL string. - - .. seealso:: - - :func:`.or_` - - """ - return BooleanClauseList.and_(*clauses) - - -def any_(expr: _ColumnExpressionArgument[_T]) -> CollectionAggregate[bool]: - """Produce an ANY expression. - - For dialects such as that of PostgreSQL, this operator applies - to usage of the :class:`_types.ARRAY` datatype, for that of - MySQL, it may apply to a subquery. e.g.:: - - # renders on PostgreSQL: - # '5 = ANY (somearray)' - expr = 5 == any_(mytable.c.somearray) - - # renders on MySQL: - # '5 = ANY (SELECT value FROM table)' - expr = 5 == any_(select(table.c.value)) - - Comparison to NULL may work using ``None`` or :func:`_sql.null`:: - - None == any_(mytable.c.somearray) - - The any_() / all_() operators also feature a special "operand flipping" - behavior such that if any_() / all_() are used on the left side of a - comparison using a standalone operator such as ``==``, ``!=``, etc. - (not including operator methods such as - :meth:`_sql.ColumnOperators.is_`) the rendered expression is flipped:: - - # would render '5 = ANY (column)` - any_(mytable.c.column) == 5 - - Or with ``None``, which note will not perform - the usual step of rendering "IS" as is normally the case for NULL:: - - # would render 'NULL = ANY(somearray)' - any_(mytable.c.somearray) == None - - .. versionchanged:: 1.4.26 repaired the use of any_() / all_() - comparing to NULL on the right side to be flipped to the left. - - The column-level :meth:`_sql.ColumnElement.any_` method (not to be - confused with :class:`_types.ARRAY` level - :meth:`_types.ARRAY.Comparator.any`) is shorthand for - ``any_(col)``:: - - 5 = mytable.c.somearray.any_() - - .. seealso:: - - :meth:`_sql.ColumnOperators.any_` - - :func:`_expression.all_` - - """ - return CollectionAggregate._create_any(expr) - - -def asc( - column: _ColumnExpressionOrStrLabelArgument[_T], -) -> UnaryExpression[_T]: - """Produce an ascending ``ORDER BY`` clause element. - - e.g.:: - - from sqlalchemy import asc - stmt = select(users_table).order_by(asc(users_table.c.name)) - - will produce SQL as:: - - SELECT id, name FROM user ORDER BY name ASC - - The :func:`.asc` function is a standalone version of the - :meth:`_expression.ColumnElement.asc` - method available on all SQL expressions, - e.g.:: - - - stmt = select(users_table).order_by(users_table.c.name.asc()) - - :param column: A :class:`_expression.ColumnElement` (e.g. - scalar SQL expression) - with which to apply the :func:`.asc` operation. - - .. seealso:: - - :func:`.desc` - - :func:`.nulls_first` - - :func:`.nulls_last` - - :meth:`_expression.Select.order_by` - - """ - return UnaryExpression._create_asc(column) - - -def collate( - expression: _ColumnExpressionArgument[str], collation: str -) -> BinaryExpression[str]: - """Return the clause ``expression COLLATE collation``. - - e.g.:: - - collate(mycolumn, 'utf8_bin') - - produces:: - - mycolumn COLLATE utf8_bin - - The collation expression is also quoted if it is a case sensitive - identifier, e.g. contains uppercase characters. - - .. versionchanged:: 1.2 quoting is automatically applied to COLLATE - expressions if they are case sensitive. - - """ - return CollationClause._create_collation_expression(expression, collation) - - -def between( - expr: _ColumnExpressionOrLiteralArgument[_T], - lower_bound: Any, - upper_bound: Any, - symmetric: bool = False, -) -> BinaryExpression[bool]: - """Produce a ``BETWEEN`` predicate clause. - - E.g.:: - - from sqlalchemy import between - stmt = select(users_table).where(between(users_table.c.id, 5, 7)) - - Would produce SQL resembling:: - - SELECT id, name FROM user WHERE id BETWEEN :id_1 AND :id_2 - - The :func:`.between` function is a standalone version of the - :meth:`_expression.ColumnElement.between` method available on all - SQL expressions, as in:: - - stmt = select(users_table).where(users_table.c.id.between(5, 7)) - - All arguments passed to :func:`.between`, including the left side - column expression, are coerced from Python scalar values if a - the value is not a :class:`_expression.ColumnElement` subclass. - For example, - three fixed values can be compared as in:: - - print(between(5, 3, 7)) - - Which would produce:: - - :param_1 BETWEEN :param_2 AND :param_3 - - :param expr: a column expression, typically a - :class:`_expression.ColumnElement` - instance or alternatively a Python scalar expression to be coerced - into a column expression, serving as the left side of the ``BETWEEN`` - expression. - - :param lower_bound: a column or Python scalar expression serving as the - lower bound of the right side of the ``BETWEEN`` expression. - - :param upper_bound: a column or Python scalar expression serving as the - upper bound of the right side of the ``BETWEEN`` expression. - - :param symmetric: if True, will render " BETWEEN SYMMETRIC ". Note - that not all databases support this syntax. - - .. seealso:: - - :meth:`_expression.ColumnElement.between` - - """ - col_expr = coercions.expect(roles.ExpressionElementRole, expr) - return col_expr.between(lower_bound, upper_bound, symmetric=symmetric) - - -def outparam( - key: str, type_: Optional[TypeEngine[_T]] = None -) -> BindParameter[_T]: - """Create an 'OUT' parameter for usage in functions (stored procedures), - for databases which support them. - - The ``outparam`` can be used like a regular function parameter. - The "output" value will be available from the - :class:`~sqlalchemy.engine.CursorResult` object via its ``out_parameters`` - attribute, which returns a dictionary containing the values. - - """ - return BindParameter(key, None, type_=type_, unique=False, isoutparam=True) - - -@overload -def not_(clause: BinaryExpression[_T]) -> BinaryExpression[_T]: ... - - -@overload -def not_(clause: _ColumnExpressionArgument[_T]) -> ColumnElement[_T]: ... - - -def not_(clause: _ColumnExpressionArgument[_T]) -> ColumnElement[_T]: - """Return a negation of the given clause, i.e. ``NOT(clause)``. - - The ``~`` operator is also overloaded on all - :class:`_expression.ColumnElement` subclasses to produce the - same result. - - """ - - return coercions.expect(roles.ExpressionElementRole, clause).__invert__() - - -def bindparam( - key: Optional[str], - value: Any = _NoArg.NO_ARG, - type_: Optional[_TypeEngineArgument[_T]] = None, - unique: bool = False, - required: Union[bool, Literal[_NoArg.NO_ARG]] = _NoArg.NO_ARG, - quote: Optional[bool] = None, - callable_: Optional[Callable[[], Any]] = None, - expanding: bool = False, - isoutparam: bool = False, - literal_execute: bool = False, -) -> BindParameter[_T]: - r"""Produce a "bound expression". - - The return value is an instance of :class:`.BindParameter`; this - is a :class:`_expression.ColumnElement` - subclass which represents a so-called - "placeholder" value in a SQL expression, the value of which is - supplied at the point at which the statement in executed against a - database connection. - - In SQLAlchemy, the :func:`.bindparam` construct has - the ability to carry along the actual value that will be ultimately - used at expression time. In this way, it serves not just as - a "placeholder" for eventual population, but also as a means of - representing so-called "unsafe" values which should not be rendered - directly in a SQL statement, but rather should be passed along - to the :term:`DBAPI` as values which need to be correctly escaped - and potentially handled for type-safety. - - When using :func:`.bindparam` explicitly, the use case is typically - one of traditional deferment of parameters; the :func:`.bindparam` - construct accepts a name which can then be referred to at execution - time:: - - from sqlalchemy import bindparam - - stmt = select(users_table).where( - users_table.c.name == bindparam("username") - ) - - The above statement, when rendered, will produce SQL similar to:: - - SELECT id, name FROM user WHERE name = :username - - In order to populate the value of ``:username`` above, the value - would typically be applied at execution time to a method - like :meth:`_engine.Connection.execute`:: - - result = connection.execute(stmt, {"username": "wendy"}) - - Explicit use of :func:`.bindparam` is also common when producing - UPDATE or DELETE statements that are to be invoked multiple times, - where the WHERE criterion of the statement is to change on each - invocation, such as:: - - stmt = ( - users_table.update() - .where(user_table.c.name == bindparam("username")) - .values(fullname=bindparam("fullname")) - ) - - connection.execute( - stmt, - [ - {"username": "wendy", "fullname": "Wendy Smith"}, - {"username": "jack", "fullname": "Jack Jones"}, - ], - ) - - SQLAlchemy's Core expression system makes wide use of - :func:`.bindparam` in an implicit sense. It is typical that Python - literal values passed to virtually all SQL expression functions are - coerced into fixed :func:`.bindparam` constructs. For example, given - a comparison operation such as:: - - expr = users_table.c.name == 'Wendy' - - The above expression will produce a :class:`.BinaryExpression` - construct, where the left side is the :class:`_schema.Column` object - representing the ``name`` column, and the right side is a - :class:`.BindParameter` representing the literal value:: - - print(repr(expr.right)) - BindParameter('%(4327771088 name)s', 'Wendy', type_=String()) - - The expression above will render SQL such as:: - - user.name = :name_1 - - Where the ``:name_1`` parameter name is an anonymous name. The - actual string ``Wendy`` is not in the rendered string, but is carried - along where it is later used within statement execution. If we - invoke a statement like the following:: - - stmt = select(users_table).where(users_table.c.name == 'Wendy') - result = connection.execute(stmt) - - We would see SQL logging output as:: - - SELECT "user".id, "user".name - FROM "user" - WHERE "user".name = %(name_1)s - {'name_1': 'Wendy'} - - Above, we see that ``Wendy`` is passed as a parameter to the database, - while the placeholder ``:name_1`` is rendered in the appropriate form - for the target database, in this case the PostgreSQL database. - - Similarly, :func:`.bindparam` is invoked automatically when working - with :term:`CRUD` statements as far as the "VALUES" portion is - concerned. The :func:`_expression.insert` construct produces an - ``INSERT`` expression which will, at statement execution time, generate - bound placeholders based on the arguments passed, as in:: - - stmt = users_table.insert() - result = connection.execute(stmt, {"name": "Wendy"}) - - The above will produce SQL output as:: - - INSERT INTO "user" (name) VALUES (%(name)s) - {'name': 'Wendy'} - - The :class:`_expression.Insert` construct, at - compilation/execution time, rendered a single :func:`.bindparam` - mirroring the column name ``name`` as a result of the single ``name`` - parameter we passed to the :meth:`_engine.Connection.execute` method. - - :param key: - the key (e.g. the name) for this bind param. - Will be used in the generated - SQL statement for dialects that use named parameters. This - value may be modified when part of a compilation operation, - if other :class:`BindParameter` objects exist with the same - key, or if its length is too long and truncation is - required. - - If omitted, an "anonymous" name is generated for the bound parameter; - when given a value to bind, the end result is equivalent to calling upon - the :func:`.literal` function with a value to bind, particularly - if the :paramref:`.bindparam.unique` parameter is also provided. - - :param value: - Initial value for this bind param. Will be used at statement - execution time as the value for this parameter passed to the - DBAPI, if no other value is indicated to the statement execution - method for this particular parameter name. Defaults to ``None``. - - :param callable\_: - A callable function that takes the place of "value". The function - will be called at statement execution time to determine the - ultimate value. Used for scenarios where the actual bind - value cannot be determined at the point at which the clause - construct is created, but embedded bind values are still desirable. - - :param type\_: - A :class:`.TypeEngine` class or instance representing an optional - datatype for this :func:`.bindparam`. If not passed, a type - may be determined automatically for the bind, based on the given - value; for example, trivial Python types such as ``str``, - ``int``, ``bool`` - may result in the :class:`.String`, :class:`.Integer` or - :class:`.Boolean` types being automatically selected. - - The type of a :func:`.bindparam` is significant especially in that - the type will apply pre-processing to the value before it is - passed to the database. For example, a :func:`.bindparam` which - refers to a datetime value, and is specified as holding the - :class:`.DateTime` type, may apply conversion needed to the - value (such as stringification on SQLite) before passing the value - to the database. - - :param unique: - if True, the key name of this :class:`.BindParameter` will be - modified if another :class:`.BindParameter` of the same name - already has been located within the containing - expression. This flag is used generally by the internals - when producing so-called "anonymous" bound expressions, it - isn't generally applicable to explicitly-named :func:`.bindparam` - constructs. - - :param required: - If ``True``, a value is required at execution time. If not passed, - it defaults to ``True`` if neither :paramref:`.bindparam.value` - or :paramref:`.bindparam.callable` were passed. If either of these - parameters are present, then :paramref:`.bindparam.required` - defaults to ``False``. - - :param quote: - True if this parameter name requires quoting and is not - currently known as a SQLAlchemy reserved word; this currently - only applies to the Oracle backend, where bound names must - sometimes be quoted. - - :param isoutparam: - if True, the parameter should be treated like a stored procedure - "OUT" parameter. This applies to backends such as Oracle which - support OUT parameters. - - :param expanding: - if True, this parameter will be treated as an "expanding" parameter - at execution time; the parameter value is expected to be a sequence, - rather than a scalar value, and the string SQL statement will - be transformed on a per-execution basis to accommodate the sequence - with a variable number of parameter slots passed to the DBAPI. - This is to allow statement caching to be used in conjunction with - an IN clause. - - .. seealso:: - - :meth:`.ColumnOperators.in_` - - :ref:`baked_in` - with baked queries - - .. note:: The "expanding" feature does not support "executemany"- - style parameter sets. - - .. versionadded:: 1.2 - - .. versionchanged:: 1.3 the "expanding" bound parameter feature now - supports empty lists. - - :param literal_execute: - if True, the bound parameter will be rendered in the compile phase - with a special "POSTCOMPILE" token, and the SQLAlchemy compiler will - render the final value of the parameter into the SQL statement at - statement execution time, omitting the value from the parameter - dictionary / list passed to DBAPI ``cursor.execute()``. This - produces a similar effect as that of using the ``literal_binds``, - compilation flag, however takes place as the statement is sent to - the DBAPI ``cursor.execute()`` method, rather than when the statement - is compiled. The primary use of this - capability is for rendering LIMIT / OFFSET clauses for database - drivers that can't accommodate for bound parameters in these - contexts, while allowing SQL constructs to be cacheable at the - compilation level. - - .. versionadded:: 1.4 Added "post compile" bound parameters - - .. seealso:: - - :ref:`change_4808`. - - .. seealso:: - - :ref:`tutorial_sending_parameters` - in the - :ref:`unified_tutorial` - - - """ - return BindParameter( - key, - value, - type_, - unique, - required, - quote, - callable_, - expanding, - isoutparam, - literal_execute, - ) - - -def case( - *whens: Union[ - typing_Tuple[_ColumnExpressionArgument[bool], Any], Mapping[Any, Any] - ], - value: Optional[Any] = None, - else_: Optional[Any] = None, -) -> Case[Any]: - r"""Produce a ``CASE`` expression. - - The ``CASE`` construct in SQL is a conditional object that - acts somewhat analogously to an "if/then" construct in other - languages. It returns an instance of :class:`.Case`. - - :func:`.case` in its usual form is passed a series of "when" - constructs, that is, a list of conditions and results as tuples:: - - from sqlalchemy import case - - stmt = select(users_table).\ - where( - case( - (users_table.c.name == 'wendy', 'W'), - (users_table.c.name == 'jack', 'J'), - else_='E' - ) - ) - - The above statement will produce SQL resembling:: - - SELECT id, name FROM user - WHERE CASE - WHEN (name = :name_1) THEN :param_1 - WHEN (name = :name_2) THEN :param_2 - ELSE :param_3 - END - - When simple equality expressions of several values against a single - parent column are needed, :func:`.case` also has a "shorthand" format - used via the - :paramref:`.case.value` parameter, which is passed a column - expression to be compared. In this form, the :paramref:`.case.whens` - parameter is passed as a dictionary containing expressions to be - compared against keyed to result expressions. The statement below is - equivalent to the preceding statement:: - - stmt = select(users_table).\ - where( - case( - {"wendy": "W", "jack": "J"}, - value=users_table.c.name, - else_='E' - ) - ) - - The values which are accepted as result values in - :paramref:`.case.whens` as well as with :paramref:`.case.else_` are - coerced from Python literals into :func:`.bindparam` constructs. - SQL expressions, e.g. :class:`_expression.ColumnElement` constructs, - are accepted - as well. To coerce a literal string expression into a constant - expression rendered inline, use the :func:`_expression.literal_column` - construct, - as in:: - - from sqlalchemy import case, literal_column - - case( - ( - orderline.c.qty > 100, - literal_column("'greaterthan100'") - ), - ( - orderline.c.qty > 10, - literal_column("'greaterthan10'") - ), - else_=literal_column("'lessthan10'") - ) - - The above will render the given constants without using bound - parameters for the result values (but still for the comparison - values), as in:: - - CASE - WHEN (orderline.qty > :qty_1) THEN 'greaterthan100' - WHEN (orderline.qty > :qty_2) THEN 'greaterthan10' - ELSE 'lessthan10' - END - - :param \*whens: The criteria to be compared against, - :paramref:`.case.whens` accepts two different forms, based on - whether or not :paramref:`.case.value` is used. - - .. versionchanged:: 1.4 the :func:`_sql.case` - function now accepts the series of WHEN conditions positionally - - In the first form, it accepts multiple 2-tuples passed as positional - arguments; each 2-tuple consists of ``(<sql expression>, <value>)``, - where the SQL expression is a boolean expression and "value" is a - resulting value, e.g.:: - - case( - (users_table.c.name == 'wendy', 'W'), - (users_table.c.name == 'jack', 'J') - ) - - In the second form, it accepts a Python dictionary of comparison - values mapped to a resulting value; this form requires - :paramref:`.case.value` to be present, and values will be compared - using the ``==`` operator, e.g.:: - - case( - {"wendy": "W", "jack": "J"}, - value=users_table.c.name - ) - - :param value: An optional SQL expression which will be used as a - fixed "comparison point" for candidate values within a dictionary - passed to :paramref:`.case.whens`. - - :param else\_: An optional SQL expression which will be the evaluated - result of the ``CASE`` construct if all expressions within - :paramref:`.case.whens` evaluate to false. When omitted, most - databases will produce a result of NULL if none of the "when" - expressions evaluate to true. - - - """ - return Case(*whens, value=value, else_=else_) - - -def cast( - expression: _ColumnExpressionOrLiteralArgument[Any], - type_: _TypeEngineArgument[_T], -) -> Cast[_T]: - r"""Produce a ``CAST`` expression. - - :func:`.cast` returns an instance of :class:`.Cast`. - - E.g.:: - - from sqlalchemy import cast, Numeric - - stmt = select(cast(product_table.c.unit_price, Numeric(10, 4))) - - The above statement will produce SQL resembling:: - - SELECT CAST(unit_price AS NUMERIC(10, 4)) FROM product - - The :func:`.cast` function performs two distinct functions when - used. The first is that it renders the ``CAST`` expression within - the resulting SQL string. The second is that it associates the given - type (e.g. :class:`.TypeEngine` class or instance) with the column - expression on the Python side, which means the expression will take - on the expression operator behavior associated with that type, - as well as the bound-value handling and result-row-handling behavior - of the type. - - An alternative to :func:`.cast` is the :func:`.type_coerce` function. - This function performs the second task of associating an expression - with a specific type, but does not render the ``CAST`` expression - in SQL. - - :param expression: A SQL expression, such as a - :class:`_expression.ColumnElement` - expression or a Python string which will be coerced into a bound - literal value. - - :param type\_: A :class:`.TypeEngine` class or instance indicating - the type to which the ``CAST`` should apply. - - .. seealso:: - - :ref:`tutorial_casts` - - :func:`.try_cast` - an alternative to CAST that results in - NULLs when the cast fails, instead of raising an error. - Only supported by some dialects. - - :func:`.type_coerce` - an alternative to CAST that coerces the type - on the Python side only, which is often sufficient to generate the - correct SQL and data coercion. - - - """ - return Cast(expression, type_) - - -def try_cast( - expression: _ColumnExpressionOrLiteralArgument[Any], - type_: _TypeEngineArgument[_T], -) -> TryCast[_T]: - """Produce a ``TRY_CAST`` expression for backends which support it; - this is a ``CAST`` which returns NULL for un-castable conversions. - - In SQLAlchemy, this construct is supported **only** by the SQL Server - dialect, and will raise a :class:`.CompileError` if used on other - included backends. However, third party backends may also support - this construct. - - .. tip:: As :func:`_sql.try_cast` originates from the SQL Server dialect, - it's importable both from ``sqlalchemy.`` as well as from - ``sqlalchemy.dialects.mssql``. - - :func:`_sql.try_cast` returns an instance of :class:`.TryCast` and - generally behaves similarly to the :class:`.Cast` construct; - at the SQL level, the difference between ``CAST`` and ``TRY_CAST`` - is that ``TRY_CAST`` returns NULL for an un-castable expression, - such as attempting to cast a string ``"hi"`` to an integer value. - - E.g.:: - - from sqlalchemy import select, try_cast, Numeric - - stmt = select( - try_cast(product_table.c.unit_price, Numeric(10, 4)) - ) - - The above would render on Microsoft SQL Server as:: - - SELECT TRY_CAST (product_table.unit_price AS NUMERIC(10, 4)) - FROM product_table - - .. versionadded:: 2.0.14 :func:`.try_cast` has been - generalized from the SQL Server dialect into a general use - construct that may be supported by additional dialects. - - """ - return TryCast(expression, type_) - - -def column( - text: str, - type_: Optional[_TypeEngineArgument[_T]] = None, - is_literal: bool = False, - _selectable: Optional[FromClause] = None, -) -> ColumnClause[_T]: - """Produce a :class:`.ColumnClause` object. - - The :class:`.ColumnClause` is a lightweight analogue to the - :class:`_schema.Column` class. The :func:`_expression.column` - function can - be invoked with just a name alone, as in:: - - from sqlalchemy import column - - id, name = column("id"), column("name") - stmt = select(id, name).select_from("user") - - The above statement would produce SQL like:: - - SELECT id, name FROM user - - Once constructed, :func:`_expression.column` - may be used like any other SQL - expression element such as within :func:`_expression.select` - constructs:: - - from sqlalchemy.sql import column - - id, name = column("id"), column("name") - stmt = select(id, name).select_from("user") - - The text handled by :func:`_expression.column` - is assumed to be handled - like the name of a database column; if the string contains mixed case, - special characters, or matches a known reserved word on the target - backend, the column expression will render using the quoting - behavior determined by the backend. To produce a textual SQL - expression that is rendered exactly without any quoting, - use :func:`_expression.literal_column` instead, - or pass ``True`` as the - value of :paramref:`_expression.column.is_literal`. Additionally, - full SQL - statements are best handled using the :func:`_expression.text` - construct. - - :func:`_expression.column` can be used in a table-like - fashion by combining it with the :func:`.table` function - (which is the lightweight analogue to :class:`_schema.Table` - ) to produce - a working table construct with minimal boilerplate:: - - from sqlalchemy import table, column, select - - user = table("user", - column("id"), - column("name"), - column("description"), - ) - - stmt = select(user.c.description).where(user.c.name == 'wendy') - - A :func:`_expression.column` / :func:`.table` - construct like that illustrated - above can be created in an - ad-hoc fashion and is not associated with any - :class:`_schema.MetaData`, DDL, or events, unlike its - :class:`_schema.Table` counterpart. - - :param text: the text of the element. - - :param type: :class:`_types.TypeEngine` object which can associate - this :class:`.ColumnClause` with a type. - - :param is_literal: if True, the :class:`.ColumnClause` is assumed to - be an exact expression that will be delivered to the output with no - quoting rules applied regardless of case sensitive settings. the - :func:`_expression.literal_column()` function essentially invokes - :func:`_expression.column` while passing ``is_literal=True``. - - .. seealso:: - - :class:`_schema.Column` - - :func:`_expression.literal_column` - - :func:`.table` - - :func:`_expression.text` - - :ref:`tutorial_select_arbitrary_text` - - """ - return ColumnClause(text, type_, is_literal, _selectable) - - -def desc( - column: _ColumnExpressionOrStrLabelArgument[_T], -) -> UnaryExpression[_T]: - """Produce a descending ``ORDER BY`` clause element. - - e.g.:: - - from sqlalchemy import desc - - stmt = select(users_table).order_by(desc(users_table.c.name)) - - will produce SQL as:: - - SELECT id, name FROM user ORDER BY name DESC - - The :func:`.desc` function is a standalone version of the - :meth:`_expression.ColumnElement.desc` - method available on all SQL expressions, - e.g.:: - - - stmt = select(users_table).order_by(users_table.c.name.desc()) - - :param column: A :class:`_expression.ColumnElement` (e.g. - scalar SQL expression) - with which to apply the :func:`.desc` operation. - - .. seealso:: - - :func:`.asc` - - :func:`.nulls_first` - - :func:`.nulls_last` - - :meth:`_expression.Select.order_by` - - """ - return UnaryExpression._create_desc(column) - - -def distinct(expr: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]: - """Produce an column-expression-level unary ``DISTINCT`` clause. - - This applies the ``DISTINCT`` keyword to an individual column - expression, and is typically contained within an aggregate function, - as in:: - - from sqlalchemy import distinct, func - stmt = select(func.count(distinct(users_table.c.name))) - - The above would produce an expression resembling:: - - SELECT COUNT(DISTINCT name) FROM user - - The :func:`.distinct` function is also available as a column-level - method, e.g. :meth:`_expression.ColumnElement.distinct`, as in:: - - stmt = select(func.count(users_table.c.name.distinct())) - - The :func:`.distinct` operator is different from the - :meth:`_expression.Select.distinct` method of - :class:`_expression.Select`, - which produces a ``SELECT`` statement - with ``DISTINCT`` applied to the result set as a whole, - e.g. a ``SELECT DISTINCT`` expression. See that method for further - information. - - .. seealso:: - - :meth:`_expression.ColumnElement.distinct` - - :meth:`_expression.Select.distinct` - - :data:`.func` - - """ - return UnaryExpression._create_distinct(expr) - - -def bitwise_not(expr: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]: - """Produce a unary bitwise NOT clause, typically via the ``~`` operator. - - Not to be confused with boolean negation :func:`_sql.not_`. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - - """ - - return UnaryExpression._create_bitwise_not(expr) - - -def extract(field: str, expr: _ColumnExpressionArgument[Any]) -> Extract: - """Return a :class:`.Extract` construct. - - This is typically available as :func:`.extract` - as well as ``func.extract`` from the - :data:`.func` namespace. - - :param field: The field to extract. - - :param expr: A column or Python scalar expression serving as the - right side of the ``EXTRACT`` expression. - - E.g.:: - - from sqlalchemy import extract - from sqlalchemy import table, column - - logged_table = table("user", - column("id"), - column("date_created"), - ) - - stmt = select(logged_table.c.id).where( - extract("YEAR", logged_table.c.date_created) == 2021 - ) - - In the above example, the statement is used to select ids from the - database where the ``YEAR`` component matches a specific value. - - Similarly, one can also select an extracted component:: - - stmt = select( - extract("YEAR", logged_table.c.date_created) - ).where(logged_table.c.id == 1) - - The implementation of ``EXTRACT`` may vary across database backends. - Users are reminded to consult their database documentation. - """ - return Extract(field, expr) - - -def false() -> False_: - """Return a :class:`.False_` construct. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import false - >>> print(select(t.c.x).where(false())) - {printsql}SELECT x FROM t WHERE false - - A backend which does not support true/false constants will render as - an expression against 1 or 0: - - .. sourcecode:: pycon+sql - - >>> print(select(t.c.x).where(false())) - {printsql}SELECT x FROM t WHERE 0 = 1 - - The :func:`.true` and :func:`.false` constants also feature - "short circuit" operation within an :func:`.and_` or :func:`.or_` - conjunction: - - .. sourcecode:: pycon+sql - - >>> print(select(t.c.x).where(or_(t.c.x > 5, true()))) - {printsql}SELECT x FROM t WHERE true{stop} - - >>> print(select(t.c.x).where(and_(t.c.x > 5, false()))) - {printsql}SELECT x FROM t WHERE false{stop} - - .. seealso:: - - :func:`.true` - - """ - - return False_._instance() - - -def funcfilter( - func: FunctionElement[_T], *criterion: _ColumnExpressionArgument[bool] -) -> FunctionFilter[_T]: - """Produce a :class:`.FunctionFilter` object against a function. - - Used against aggregate and window functions, - for database backends that support the "FILTER" clause. - - E.g.:: - - from sqlalchemy import funcfilter - funcfilter(func.count(1), MyClass.name == 'some name') - - Would produce "COUNT(1) FILTER (WHERE myclass.name = 'some name')". - - This function is also available from the :data:`~.expression.func` - construct itself via the :meth:`.FunctionElement.filter` method. - - .. seealso:: - - :ref:`tutorial_functions_within_group` - in the - :ref:`unified_tutorial` - - :meth:`.FunctionElement.filter` - - """ - return FunctionFilter(func, *criterion) - - -def label( - name: str, - element: _ColumnExpressionArgument[_T], - type_: Optional[_TypeEngineArgument[_T]] = None, -) -> Label[_T]: - """Return a :class:`Label` object for the - given :class:`_expression.ColumnElement`. - - A label changes the name of an element in the columns clause of a - ``SELECT`` statement, typically via the ``AS`` SQL keyword. - - This functionality is more conveniently available via the - :meth:`_expression.ColumnElement.label` method on - :class:`_expression.ColumnElement`. - - :param name: label name - - :param obj: a :class:`_expression.ColumnElement`. - - """ - return Label(name, element, type_) - - -def null() -> Null: - """Return a constant :class:`.Null` construct.""" - - return Null._instance() - - -def nulls_first(column: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]: - """Produce the ``NULLS FIRST`` modifier for an ``ORDER BY`` expression. - - :func:`.nulls_first` is intended to modify the expression produced - by :func:`.asc` or :func:`.desc`, and indicates how NULL values - should be handled when they are encountered during ordering:: - - - from sqlalchemy import desc, nulls_first - - stmt = select(users_table).order_by( - nulls_first(desc(users_table.c.name))) - - The SQL expression from the above would resemble:: - - SELECT id, name FROM user ORDER BY name DESC NULLS FIRST - - Like :func:`.asc` and :func:`.desc`, :func:`.nulls_first` is typically - invoked from the column expression itself using - :meth:`_expression.ColumnElement.nulls_first`, - rather than as its standalone - function version, as in:: - - stmt = select(users_table).order_by( - users_table.c.name.desc().nulls_first()) - - .. versionchanged:: 1.4 :func:`.nulls_first` is renamed from - :func:`.nullsfirst` in previous releases. - The previous name remains available for backwards compatibility. - - .. seealso:: - - :func:`.asc` - - :func:`.desc` - - :func:`.nulls_last` - - :meth:`_expression.Select.order_by` - - """ - return UnaryExpression._create_nulls_first(column) - - -def nulls_last(column: _ColumnExpressionArgument[_T]) -> UnaryExpression[_T]: - """Produce the ``NULLS LAST`` modifier for an ``ORDER BY`` expression. - - :func:`.nulls_last` is intended to modify the expression produced - by :func:`.asc` or :func:`.desc`, and indicates how NULL values - should be handled when they are encountered during ordering:: - - - from sqlalchemy import desc, nulls_last - - stmt = select(users_table).order_by( - nulls_last(desc(users_table.c.name))) - - The SQL expression from the above would resemble:: - - SELECT id, name FROM user ORDER BY name DESC NULLS LAST - - Like :func:`.asc` and :func:`.desc`, :func:`.nulls_last` is typically - invoked from the column expression itself using - :meth:`_expression.ColumnElement.nulls_last`, - rather than as its standalone - function version, as in:: - - stmt = select(users_table).order_by( - users_table.c.name.desc().nulls_last()) - - .. versionchanged:: 1.4 :func:`.nulls_last` is renamed from - :func:`.nullslast` in previous releases. - The previous name remains available for backwards compatibility. - - .. seealso:: - - :func:`.asc` - - :func:`.desc` - - :func:`.nulls_first` - - :meth:`_expression.Select.order_by` - - """ - return UnaryExpression._create_nulls_last(column) - - -def or_( # type: ignore[empty-body] - initial_clause: Union[Literal[False], _ColumnExpressionArgument[bool]], - *clauses: _ColumnExpressionArgument[bool], -) -> ColumnElement[bool]: - """Produce a conjunction of expressions joined by ``OR``. - - E.g.:: - - from sqlalchemy import or_ - - stmt = select(users_table).where( - or_( - users_table.c.name == 'wendy', - users_table.c.name == 'jack' - ) - ) - - The :func:`.or_` conjunction is also available using the - Python ``|`` operator (though note that compound expressions - need to be parenthesized in order to function with Python - operator precedence behavior):: - - stmt = select(users_table).where( - (users_table.c.name == 'wendy') | - (users_table.c.name == 'jack') - ) - - The :func:`.or_` construct must be given at least one positional - argument in order to be valid; a :func:`.or_` construct with no - arguments is ambiguous. To produce an "empty" or dynamically - generated :func:`.or_` expression, from a given list of expressions, - a "default" element of :func:`_sql.false` (or just ``False``) should be - specified:: - - from sqlalchemy import false - or_criteria = or_(false(), *expressions) - - The above expression will compile to SQL as the expression ``false`` - or ``0 = 1``, depending on backend, if no other expressions are - present. If expressions are present, then the :func:`_sql.false` value is - ignored as it does not affect the outcome of an OR expression which - has other elements. - - .. deprecated:: 1.4 The :func:`.or_` element now requires that at - least one argument is passed; creating the :func:`.or_` construct - with no arguments is deprecated, and will emit a deprecation warning - while continuing to produce a blank SQL string. - - .. seealso:: - - :func:`.and_` - - """ - ... - - -if not TYPE_CHECKING: - # handle deprecated case which allows zero-arguments - def or_(*clauses): # noqa: F811 - """Produce a conjunction of expressions joined by ``OR``. - - E.g.:: - - from sqlalchemy import or_ - - stmt = select(users_table).where( - or_( - users_table.c.name == 'wendy', - users_table.c.name == 'jack' - ) - ) - - The :func:`.or_` conjunction is also available using the - Python ``|`` operator (though note that compound expressions - need to be parenthesized in order to function with Python - operator precedence behavior):: - - stmt = select(users_table).where( - (users_table.c.name == 'wendy') | - (users_table.c.name == 'jack') - ) - - The :func:`.or_` construct must be given at least one positional - argument in order to be valid; a :func:`.or_` construct with no - arguments is ambiguous. To produce an "empty" or dynamically - generated :func:`.or_` expression, from a given list of expressions, - a "default" element of :func:`_sql.false` (or just ``False``) should be - specified:: - - from sqlalchemy import false - or_criteria = or_(false(), *expressions) - - The above expression will compile to SQL as the expression ``false`` - or ``0 = 1``, depending on backend, if no other expressions are - present. If expressions are present, then the :func:`_sql.false` value - is ignored as it does not affect the outcome of an OR expression which - has other elements. - - .. deprecated:: 1.4 The :func:`.or_` element now requires that at - least one argument is passed; creating the :func:`.or_` construct - with no arguments is deprecated, and will emit a deprecation warning - while continuing to produce a blank SQL string. - - .. seealso:: - - :func:`.and_` - - """ - return BooleanClauseList.or_(*clauses) - - -def over( - element: FunctionElement[_T], - partition_by: Optional[_ByArgument] = None, - order_by: Optional[_ByArgument] = None, - range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, - rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, -) -> Over[_T]: - r"""Produce an :class:`.Over` object against a function. - - Used against aggregate or so-called "window" functions, - for database backends that support window functions. - - :func:`_expression.over` is usually called using - the :meth:`.FunctionElement.over` method, e.g.:: - - func.row_number().over(order_by=mytable.c.some_column) - - Would produce:: - - ROW_NUMBER() OVER(ORDER BY some_column) - - Ranges are also possible using the :paramref:`.expression.over.range_` - and :paramref:`.expression.over.rows` parameters. These - mutually-exclusive parameters each accept a 2-tuple, which contains - a combination of integers and None:: - - func.row_number().over( - order_by=my_table.c.some_column, range_=(None, 0)) - - The above would produce:: - - ROW_NUMBER() OVER(ORDER BY some_column - RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - - A value of ``None`` indicates "unbounded", a - value of zero indicates "current row", and negative / positive - integers indicate "preceding" and "following": - - * RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING:: - - func.row_number().over(order_by='x', range_=(-5, 10)) - - * ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:: - - func.row_number().over(order_by='x', rows=(None, 0)) - - * RANGE BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING:: - - func.row_number().over(order_by='x', range_=(-2, None)) - - * RANGE BETWEEN 1 FOLLOWING AND 3 FOLLOWING:: - - func.row_number().over(order_by='x', range_=(1, 3)) - - :param element: a :class:`.FunctionElement`, :class:`.WithinGroup`, - or other compatible construct. - :param partition_by: a column element or string, or a list - of such, that will be used as the PARTITION BY clause - of the OVER construct. - :param order_by: a column element or string, or a list - of such, that will be used as the ORDER BY clause - of the OVER construct. - :param range\_: optional range clause for the window. This is a - tuple value which can contain integer values or ``None``, - and will render a RANGE BETWEEN PRECEDING / FOLLOWING clause. - - :param rows: optional rows clause for the window. This is a tuple - value which can contain integer values or None, and will render - a ROWS BETWEEN PRECEDING / FOLLOWING clause. - - This function is also available from the :data:`~.expression.func` - construct itself via the :meth:`.FunctionElement.over` method. - - .. seealso:: - - :ref:`tutorial_window_functions` - in the :ref:`unified_tutorial` - - :data:`.expression.func` - - :func:`_expression.within_group` - - """ - return Over(element, partition_by, order_by, range_, rows) - - -@_document_text_coercion("text", ":func:`.text`", ":paramref:`.text.text`") -def text(text: str) -> TextClause: - r"""Construct a new :class:`_expression.TextClause` clause, - representing - a textual SQL string directly. - - E.g.:: - - from sqlalchemy import text - - t = text("SELECT * FROM users") - result = connection.execute(t) - - The advantages :func:`_expression.text` - provides over a plain string are - backend-neutral support for bind parameters, per-statement - execution options, as well as - bind parameter and result-column typing behavior, allowing - SQLAlchemy type constructs to play a role when executing - a statement that is specified literally. The construct can also - be provided with a ``.c`` collection of column elements, allowing - it to be embedded in other SQL expression constructs as a subquery. - - Bind parameters are specified by name, using the format ``:name``. - E.g.:: - - t = text("SELECT * FROM users WHERE id=:user_id") - result = connection.execute(t, {"user_id": 12}) - - For SQL statements where a colon is required verbatim, as within - an inline string, use a backslash to escape:: - - t = text(r"SELECT * FROM users WHERE name='\:username'") - - The :class:`_expression.TextClause` - construct includes methods which can - provide information about the bound parameters as well as the column - values which would be returned from the textual statement, assuming - it's an executable SELECT type of statement. The - :meth:`_expression.TextClause.bindparams` - method is used to provide bound - parameter detail, and :meth:`_expression.TextClause.columns` - method allows - specification of return columns including names and types:: - - t = text("SELECT * FROM users WHERE id=:user_id").\ - bindparams(user_id=7).\ - columns(id=Integer, name=String) - - for id, name in connection.execute(t): - print(id, name) - - The :func:`_expression.text` construct is used in cases when - a literal string SQL fragment is specified as part of a larger query, - such as for the WHERE clause of a SELECT statement:: - - s = select(users.c.id, users.c.name).where(text("id=:user_id")) - result = connection.execute(s, {"user_id": 12}) - - :func:`_expression.text` is also used for the construction - of a full, standalone statement using plain text. - As such, SQLAlchemy refers - to it as an :class:`.Executable` object and may be used - like any other statement passed to an ``.execute()`` method. - - :param text: - the text of the SQL statement to be created. Use ``:<param>`` - to specify bind parameters; they will be compiled to their - engine-specific format. - - .. seealso:: - - :ref:`tutorial_select_arbitrary_text` - - """ - return TextClause(text) - - -def true() -> True_: - """Return a constant :class:`.True_` construct. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import true - >>> print(select(t.c.x).where(true())) - {printsql}SELECT x FROM t WHERE true - - A backend which does not support true/false constants will render as - an expression against 1 or 0: - - .. sourcecode:: pycon+sql - - >>> print(select(t.c.x).where(true())) - {printsql}SELECT x FROM t WHERE 1 = 1 - - The :func:`.true` and :func:`.false` constants also feature - "short circuit" operation within an :func:`.and_` or :func:`.or_` - conjunction: - - .. sourcecode:: pycon+sql - - >>> print(select(t.c.x).where(or_(t.c.x > 5, true()))) - {printsql}SELECT x FROM t WHERE true{stop} - - >>> print(select(t.c.x).where(and_(t.c.x > 5, false()))) - {printsql}SELECT x FROM t WHERE false{stop} - - .. seealso:: - - :func:`.false` - - """ - - return True_._instance() - - -def tuple_( - *clauses: _ColumnExpressionArgument[Any], - types: Optional[Sequence[_TypeEngineArgument[Any]]] = None, -) -> Tuple: - """Return a :class:`.Tuple`. - - Main usage is to produce a composite IN construct using - :meth:`.ColumnOperators.in_` :: - - from sqlalchemy import tuple_ - - tuple_(table.c.col1, table.c.col2).in_( - [(1, 2), (5, 12), (10, 19)] - ) - - .. versionchanged:: 1.3.6 Added support for SQLite IN tuples. - - .. warning:: - - The composite IN construct is not supported by all backends, and is - currently known to work on PostgreSQL, MySQL, and SQLite. - Unsupported backends will raise a subclass of - :class:`~sqlalchemy.exc.DBAPIError` when such an expression is - invoked. - - """ - return Tuple(*clauses, types=types) - - -def type_coerce( - expression: _ColumnExpressionOrLiteralArgument[Any], - type_: _TypeEngineArgument[_T], -) -> TypeCoerce[_T]: - r"""Associate a SQL expression with a particular type, without rendering - ``CAST``. - - E.g.:: - - from sqlalchemy import type_coerce - - stmt = select(type_coerce(log_table.date_string, StringDateTime())) - - The above construct will produce a :class:`.TypeCoerce` object, which - does not modify the rendering in any way on the SQL side, with the - possible exception of a generated label if used in a columns clause - context: - - .. sourcecode:: sql - - SELECT date_string AS date_string FROM log - - When result rows are fetched, the ``StringDateTime`` type processor - will be applied to result rows on behalf of the ``date_string`` column. - - .. note:: the :func:`.type_coerce` construct does not render any - SQL syntax of its own, including that it does not imply - parenthesization. Please use :meth:`.TypeCoerce.self_group` - if explicit parenthesization is required. - - In order to provide a named label for the expression, use - :meth:`_expression.ColumnElement.label`:: - - stmt = select( - type_coerce(log_table.date_string, StringDateTime()).label('date') - ) - - - A type that features bound-value handling will also have that behavior - take effect when literal values or :func:`.bindparam` constructs are - passed to :func:`.type_coerce` as targets. - For example, if a type implements the - :meth:`.TypeEngine.bind_expression` - method or :meth:`.TypeEngine.bind_processor` method or equivalent, - these functions will take effect at statement compilation/execution - time when a literal value is passed, as in:: - - # bound-value handling of MyStringType will be applied to the - # literal value "some string" - stmt = select(type_coerce("some string", MyStringType)) - - When using :func:`.type_coerce` with composed expressions, note that - **parenthesis are not applied**. If :func:`.type_coerce` is being - used in an operator context where the parenthesis normally present from - CAST are necessary, use the :meth:`.TypeCoerce.self_group` method: - - .. sourcecode:: pycon+sql - - >>> some_integer = column("someint", Integer) - >>> some_string = column("somestr", String) - >>> expr = type_coerce(some_integer + 5, String) + some_string - >>> print(expr) - {printsql}someint + :someint_1 || somestr{stop} - >>> expr = type_coerce(some_integer + 5, String).self_group() + some_string - >>> print(expr) - {printsql}(someint + :someint_1) || somestr{stop} - - :param expression: A SQL expression, such as a - :class:`_expression.ColumnElement` - expression or a Python string which will be coerced into a bound - literal value. - - :param type\_: A :class:`.TypeEngine` class or instance indicating - the type to which the expression is coerced. - - .. seealso:: - - :ref:`tutorial_casts` - - :func:`.cast` - - """ # noqa - return TypeCoerce(expression, type_) - - -def within_group( - element: FunctionElement[_T], *order_by: _ColumnExpressionArgument[Any] -) -> WithinGroup[_T]: - r"""Produce a :class:`.WithinGroup` object against a function. - - Used against so-called "ordered set aggregate" and "hypothetical - set aggregate" functions, including :class:`.percentile_cont`, - :class:`.rank`, :class:`.dense_rank`, etc. - - :func:`_expression.within_group` is usually called using - the :meth:`.FunctionElement.within_group` method, e.g.:: - - from sqlalchemy import within_group - stmt = select( - department.c.id, - func.percentile_cont(0.5).within_group( - department.c.salary.desc() - ) - ) - - The above statement would produce SQL similar to - ``SELECT department.id, percentile_cont(0.5) - WITHIN GROUP (ORDER BY department.salary DESC)``. - - :param element: a :class:`.FunctionElement` construct, typically - generated by :data:`~.expression.func`. - :param \*order_by: one or more column elements that will be used - as the ORDER BY clause of the WITHIN GROUP construct. - - .. seealso:: - - :ref:`tutorial_functions_within_group` - in the - :ref:`unified_tutorial` - - :data:`.expression.func` - - :func:`_expression.over` - - """ - return WithinGroup(element, *order_by) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_orm_types.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_orm_types.py deleted file mode 100644 index bccb533..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_orm_types.py +++ /dev/null @@ -1,20 +0,0 @@ -# sql/_orm_types.py -# Copyright (C) 2022-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""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"] diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_py_util.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_py_util.py deleted file mode 100644 index df372bf..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_py_util.py +++ /dev/null @@ -1,75 +0,0 @@ -# sql/_py_util.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -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 diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_selectable_constructors.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_selectable_constructors.py deleted file mode 100644 index c2b5008..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_selectable_constructors.py +++ /dev/null @@ -1,635 +0,0 @@ -# sql/_selectable_constructors.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -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 TypeVar -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 _TypedColumnClauseArgument as _TCCA - from .functions import Function - from .selectable import CTE - from .selectable import HasCTE - from .selectable import ScalarSelect - from .selectable import SelectBase - - -_T = TypeVar("_T", bound=Any) - - -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 - ) - - -def except_( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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) - - -def except_all( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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:: - - 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) - - -def intersect( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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) - - -def intersect_all( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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:: - - 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. - - """ - - 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:: - - 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) - - -def union( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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) - - -def union_all( - *selects: _SelectStatementForCompoundArgument, -) -> CompoundSelect: - 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 - - 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) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_typing.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/_typing.py deleted file mode 100644 index c861bae..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/_typing.py +++ /dev/null @@ -1,457 +0,0 @@ -# sql/_typing.py -# Copyright (C) 2022-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -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 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", -) - -_MAYBE_ENTITY = TypeVar( - "_MAYBE_ENTITY", - roles.ColumnsClauseRole, - Literal["*", 1], - 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]", - Literal["*", 1], - 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], - 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[ - "SelectBase", 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 diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/annotation.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/annotation.py deleted file mode 100644 index db382b8..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/annotation.py +++ /dev/null @@ -1,585 +0,0 @@ -# sql/annotation.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""The :class:`.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) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/base.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/base.py deleted file mode 100644 index 5eb32e3..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/base.py +++ /dev/null @@ -1,2180 +0,0 @@ -# sql/base.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""Foundational utilities common to many sql modules. - -""" - - -from __future__ import annotations - -import collections -from enum import Enum -import itertools -from itertools import zip_longest -import operator -import re -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import FrozenSet -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping -from typing import MutableMapping -from typing import NamedTuple -from typing import NoReturn -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import roles -from . import visitors -from .cache_key import HasCacheKey # noqa -from .cache_key import MemoizedHasCacheKey # noqa -from .traversals import HasCopyInternals # noqa -from .visitors import ClauseVisitor -from .visitors import ExtendedInternalTraversal -from .visitors import ExternallyTraversible -from .visitors import InternalTraversal -from .. import event -from .. import exc -from .. import util -from ..util import HasMemoized as HasMemoized -from ..util import hybridmethod -from ..util import typing as compat_typing -from ..util.typing import Protocol -from ..util.typing import Self -from ..util.typing import TypeGuard - -if TYPE_CHECKING: - from . import coercions - from . import elements - from . import type_api - from ._orm_types import DMLStrategyArgument - from ._orm_types import SynchronizeSessionArgument - from ._typing import _CLE - from .elements import BindParameter - from .elements import ClauseList - from .elements import ColumnClause # noqa - from .elements import ColumnElement - from .elements import KeyedColumnElement - from .elements import NamedColumn - from .elements import SQLCoreOperations - from .elements import TextClause - from .schema import Column - from .schema import DefaultGenerator - from .selectable import _JoinTargetElement - from .selectable import _SelectIterable - from .selectable import FromClause - from ..engine import Connection - from ..engine import CursorResult - from ..engine.interfaces import _CoreMultiExecuteParams - from ..engine.interfaces import _ExecuteOptions - from ..engine.interfaces import _ImmutableExecuteOptions - from ..engine.interfaces import CacheStats - from ..engine.interfaces import Compiled - from ..engine.interfaces import CompiledCacheType - from ..engine.interfaces import CoreExecuteOptionsParameter - from ..engine.interfaces import Dialect - from ..engine.interfaces import IsolationLevel - from ..engine.interfaces import SchemaTranslateMapType - from ..event import dispatcher - -if not TYPE_CHECKING: - coercions = None # noqa - elements = None # noqa - type_api = None # noqa - - -class _NoArg(Enum): - NO_ARG = 0 - - def __repr__(self): - return f"_NoArg.{self.name}" - - -NO_ARG = _NoArg.NO_ARG - - -class _NoneName(Enum): - NONE_NAME = 0 - """indicate a 'deferred' name that was ultimately the value None.""" - - -_NONE_NAME = _NoneName.NONE_NAME - -_T = TypeVar("_T", bound=Any) - -_Fn = TypeVar("_Fn", bound=Callable[..., Any]) - -_AmbiguousTableNameMap = MutableMapping[str, str] - - -class _DefaultDescriptionTuple(NamedTuple): - arg: Any - is_scalar: Optional[bool] - is_callable: Optional[bool] - is_sentinel: Optional[bool] - - @classmethod - def _from_column_default( - cls, default: Optional[DefaultGenerator] - ) -> _DefaultDescriptionTuple: - return ( - _DefaultDescriptionTuple( - default.arg, # type: ignore - default.is_scalar, - default.is_callable, - default.is_sentinel, - ) - if default - and ( - default.has_arg - or (not default.for_update and default.is_sentinel) - ) - else _DefaultDescriptionTuple(None, None, None, None) - ) - - -_never_select_column = operator.attrgetter("_omit_from_statements") - - -class _EntityNamespace(Protocol): - def __getattr__(self, key: str) -> SQLCoreOperations[Any]: ... - - -class _HasEntityNamespace(Protocol): - @util.ro_non_memoized_property - def entity_namespace(self) -> _EntityNamespace: ... - - -def _is_has_entity_namespace(element: Any) -> TypeGuard[_HasEntityNamespace]: - return hasattr(element, "entity_namespace") - - -# Remove when https://github.com/python/mypy/issues/14640 will be fixed -_Self = TypeVar("_Self", bound=Any) - - -class Immutable: - """mark a ClauseElement as 'immutable' when expressions are cloned. - - "immutable" objects refers to the "mutability" of an object in the - context of SQL DQL and DML generation. Such as, in DQL, one can - compose a SELECT or subquery of varied forms, but one cannot modify - the structure of a specific table or column within DQL. - :class:`.Immutable` is mostly intended to follow this concept, and as - such the primary "immutable" objects are :class:`.ColumnClause`, - :class:`.Column`, :class:`.TableClause`, :class:`.Table`. - - """ - - __slots__ = () - - _is_immutable = True - - def unique_params(self, *optionaldict, **kwargs): - raise NotImplementedError("Immutable objects do not support copying") - - def params(self, *optionaldict, **kwargs): - raise NotImplementedError("Immutable objects do not support copying") - - def _clone(self: _Self, **kw: Any) -> _Self: - return self - - def _copy_internals( - self, *, omit_attrs: Iterable[str] = (), **kw: Any - ) -> None: - pass - - -class SingletonConstant(Immutable): - """Represent SQL constants like NULL, TRUE, FALSE""" - - _is_singleton_constant = True - - _singleton: SingletonConstant - - def __new__(cls: _T, *arg: Any, **kw: Any) -> _T: - return cast(_T, cls._singleton) - - @util.non_memoized_property - def proxy_set(self) -> FrozenSet[ColumnElement[Any]]: - raise NotImplementedError() - - @classmethod - def _create_singleton(cls): - obj = object.__new__(cls) - obj.__init__() # type: ignore - - # for a long time this was an empty frozenset, meaning - # a SingletonConstant would never be a "corresponding column" in - # a statement. This referred to #6259. However, in #7154 we see - # that we do in fact need "correspondence" to work when matching cols - # in result sets, so the non-correspondence was moved to a more - # specific level when we are actually adapting expressions for SQL - # render only. - obj.proxy_set = frozenset([obj]) - cls._singleton = obj - - -def _from_objects( - *elements: Union[ - ColumnElement[Any], FromClause, TextClause, _JoinTargetElement - ] -) -> Iterator[FromClause]: - return itertools.chain.from_iterable( - [element._from_objects for element in elements] - ) - - -def _select_iterables( - elements: Iterable[roles.ColumnsClauseRole], -) -> _SelectIterable: - """expand tables into individual columns in the - given list of column expressions. - - """ - return itertools.chain.from_iterable( - [c._select_iterable for c in elements] - ) - - -_SelfGenerativeType = TypeVar("_SelfGenerativeType", bound="_GenerativeType") - - -class _GenerativeType(compat_typing.Protocol): - def _generate(self) -> Self: ... - - -def _generative(fn: _Fn) -> _Fn: - """non-caching _generative() decorator. - - This is basically the legacy decorator that copies the object and - runs a method on the new copy. - - """ - - @util.decorator - def _generative( - fn: _Fn, self: _SelfGenerativeType, *args: Any, **kw: Any - ) -> _SelfGenerativeType: - """Mark a method as generative.""" - - self = self._generate() - x = fn(self, *args, **kw) - assert x is self, "generative methods must return self" - return self - - decorated = _generative(fn) - decorated.non_generative = fn # type: ignore - return decorated - - -def _exclusive_against(*names: str, **kw: Any) -> Callable[[_Fn], _Fn]: - msgs = kw.pop("msgs", {}) - - defaults = kw.pop("defaults", {}) - - getters = [ - (name, operator.attrgetter(name), defaults.get(name, None)) - for name in names - ] - - @util.decorator - def check(fn, *args, **kw): - # make pylance happy by not including "self" in the argument - # list - self = args[0] - args = args[1:] - for name, getter, default_ in getters: - if getter(self) is not default_: - msg = msgs.get( - name, - "Method %s() has already been invoked on this %s construct" - % (fn.__name__, self.__class__), - ) - raise exc.InvalidRequestError(msg) - return fn(self, *args, **kw) - - return check - - -def _clone(element, **kw): - return element._clone(**kw) - - -def _expand_cloned( - elements: Iterable[_CLE], -) -> Iterable[_CLE]: - """expand the given set of ClauseElements to be the set of all 'cloned' - predecessors. - - """ - # TODO: cython candidate - return itertools.chain(*[x._cloned_set for x in elements]) - - -def _de_clone( - elements: Iterable[_CLE], -) -> Iterable[_CLE]: - for x in elements: - while x._is_clone_of is not None: - x = x._is_clone_of - yield x - - -def _cloned_intersection(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]: - """return the intersection of sets a and b, counting - any overlap between 'cloned' predecessors. - - The returned set is in terms of the entities present within 'a'. - - """ - all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) - return {elem for elem in a if all_overlap.intersection(elem._cloned_set)} - - -def _cloned_difference(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]: - all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b)) - return { - elem for elem in a if not all_overlap.intersection(elem._cloned_set) - } - - -class _DialectArgView(MutableMapping[str, Any]): - """A dictionary view of dialect-level arguments in the form - <dialectname>_<argument_name>. - - """ - - def __init__(self, obj): - self.obj = obj - - def _key(self, key): - try: - dialect, value_key = key.split("_", 1) - except ValueError as err: - raise KeyError(key) from err - else: - return dialect, value_key - - def __getitem__(self, key): - dialect, value_key = self._key(key) - - try: - opt = self.obj.dialect_options[dialect] - except exc.NoSuchModuleError as err: - raise KeyError(key) from err - else: - return opt[value_key] - - def __setitem__(self, key, value): - try: - dialect, value_key = self._key(key) - except KeyError as err: - raise exc.ArgumentError( - "Keys must be of the form <dialectname>_<argname>" - ) from err - else: - self.obj.dialect_options[dialect][value_key] = value - - def __delitem__(self, key): - dialect, value_key = self._key(key) - del self.obj.dialect_options[dialect][value_key] - - def __len__(self): - return sum( - len(args._non_defaults) - for args in self.obj.dialect_options.values() - ) - - def __iter__(self): - return ( - "%s_%s" % (dialect_name, value_name) - for dialect_name in self.obj.dialect_options - for value_name in self.obj.dialect_options[ - dialect_name - ]._non_defaults - ) - - -class _DialectArgDict(MutableMapping[str, Any]): - """A dictionary view of dialect-level arguments for a specific - dialect. - - Maintains a separate collection of user-specified arguments - and dialect-specified default arguments. - - """ - - def __init__(self): - self._non_defaults = {} - self._defaults = {} - - def __len__(self): - return len(set(self._non_defaults).union(self._defaults)) - - def __iter__(self): - return iter(set(self._non_defaults).union(self._defaults)) - - def __getitem__(self, key): - if key in self._non_defaults: - return self._non_defaults[key] - else: - return self._defaults[key] - - def __setitem__(self, key, value): - self._non_defaults[key] = value - - def __delitem__(self, key): - del self._non_defaults[key] - - -@util.preload_module("sqlalchemy.dialects") -def _kw_reg_for_dialect(dialect_name): - dialect_cls = util.preloaded.dialects.registry.load(dialect_name) - if dialect_cls.construct_arguments is None: - return None - return dict(dialect_cls.construct_arguments) - - -class DialectKWArgs: - """Establish the ability for a class to have dialect-specific arguments - with defaults and constructor validation. - - The :class:`.DialectKWArgs` interacts with the - :attr:`.DefaultDialect.construct_arguments` present on a dialect. - - .. seealso:: - - :attr:`.DefaultDialect.construct_arguments` - - """ - - __slots__ = () - - _dialect_kwargs_traverse_internals = [ - ("dialect_options", InternalTraversal.dp_dialect_options) - ] - - @classmethod - def argument_for(cls, dialect_name, argument_name, default): - """Add a new kind of dialect-specific keyword argument for this class. - - E.g.:: - - Index.argument_for("mydialect", "length", None) - - some_index = Index('a', 'b', mydialect_length=5) - - The :meth:`.DialectKWArgs.argument_for` method is a per-argument - way adding extra arguments to the - :attr:`.DefaultDialect.construct_arguments` dictionary. This - dictionary provides a list of argument names accepted by various - schema-level constructs on behalf of a dialect. - - New dialects should typically specify this dictionary all at once as a - data member of the dialect class. The use case for ad-hoc addition of - argument names is typically for end-user code that is also using - a custom compilation scheme which consumes the additional arguments. - - :param dialect_name: name of a dialect. The dialect must be - locatable, else a :class:`.NoSuchModuleError` is raised. The - dialect must also include an existing - :attr:`.DefaultDialect.construct_arguments` collection, indicating - that it participates in the keyword-argument validation and default - system, else :class:`.ArgumentError` is raised. If the dialect does - not include this collection, then any keyword argument can be - specified on behalf of this dialect already. All dialects packaged - within SQLAlchemy include this collection, however for third party - dialects, support may vary. - - :param argument_name: name of the parameter. - - :param default: default value of the parameter. - - """ - - construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] - if construct_arg_dictionary is None: - raise exc.ArgumentError( - "Dialect '%s' does have keyword-argument " - "validation and defaults enabled configured" % dialect_name - ) - if cls not in construct_arg_dictionary: - construct_arg_dictionary[cls] = {} - construct_arg_dictionary[cls][argument_name] = default - - @util.memoized_property - def dialect_kwargs(self): - """A collection of keyword arguments specified as dialect-specific - options to this construct. - - The arguments are present here in their original ``<dialect>_<kwarg>`` - format. Only arguments that were actually passed are included; - unlike the :attr:`.DialectKWArgs.dialect_options` collection, which - contains all options known by this dialect including defaults. - - The collection is also writable; keys are accepted of the - form ``<dialect>_<kwarg>`` where the value will be assembled - into the list of options. - - .. seealso:: - - :attr:`.DialectKWArgs.dialect_options` - nested dictionary form - - """ - return _DialectArgView(self) - - @property - def kwargs(self): - """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`.""" - return self.dialect_kwargs - - _kw_registry = util.PopulateDict(_kw_reg_for_dialect) - - def _kw_reg_for_dialect_cls(self, dialect_name): - construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name] - d = _DialectArgDict() - - if construct_arg_dictionary is None: - d._defaults.update({"*": None}) - else: - for cls in reversed(self.__class__.__mro__): - if cls in construct_arg_dictionary: - d._defaults.update(construct_arg_dictionary[cls]) - return d - - @util.memoized_property - def dialect_options(self): - """A collection of keyword arguments specified as dialect-specific - options to this construct. - - This is a two-level nested registry, keyed to ``<dialect_name>`` - and ``<argument_name>``. For example, the ``postgresql_where`` - argument would be locatable as:: - - arg = my_object.dialect_options['postgresql']['where'] - - .. versionadded:: 0.9.2 - - .. seealso:: - - :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form - - """ - - return util.PopulateDict( - util.portable_instancemethod(self._kw_reg_for_dialect_cls) - ) - - def _validate_dialect_kwargs(self, kwargs: Dict[str, Any]) -> None: - # validate remaining kwargs that they all specify DB prefixes - - if not kwargs: - return - - for k in kwargs: - m = re.match("^(.+?)_(.+)$", k) - if not m: - raise TypeError( - "Additional arguments should be " - "named <dialectname>_<argument>, got '%s'" % k - ) - dialect_name, arg_name = m.group(1, 2) - - try: - construct_arg_dictionary = self.dialect_options[dialect_name] - except exc.NoSuchModuleError: - util.warn( - "Can't validate argument %r; can't " - "locate any SQLAlchemy dialect named %r" - % (k, dialect_name) - ) - self.dialect_options[dialect_name] = d = _DialectArgDict() - d._defaults.update({"*": None}) - d._non_defaults[arg_name] = kwargs[k] - else: - if ( - "*" not in construct_arg_dictionary - and arg_name not in construct_arg_dictionary - ): - raise exc.ArgumentError( - "Argument %r is not accepted by " - "dialect %r on behalf of %r" - % (k, dialect_name, self.__class__) - ) - else: - construct_arg_dictionary[arg_name] = kwargs[k] - - -class CompileState: - """Produces additional object state necessary for a statement to be - compiled. - - the :class:`.CompileState` class is at the base of classes that assemble - state for a particular statement object that is then used by the - compiler. This process is essentially an extension of the process that - the SQLCompiler.visit_XYZ() method takes, however there is an emphasis - on converting raw user intent into more organized structures rather than - producing string output. The top-level :class:`.CompileState` for the - statement being executed is also accessible when the execution context - works with invoking the statement and collecting results. - - The production of :class:`.CompileState` is specific to the compiler, such - as within the :meth:`.SQLCompiler.visit_insert`, - :meth:`.SQLCompiler.visit_select` etc. methods. These methods are also - responsible for associating the :class:`.CompileState` with the - :class:`.SQLCompiler` itself, if the statement is the "toplevel" statement, - i.e. the outermost SQL statement that's actually being executed. - There can be other :class:`.CompileState` objects that are not the - toplevel, such as when a SELECT subquery or CTE-nested - INSERT/UPDATE/DELETE is generated. - - .. versionadded:: 1.4 - - """ - - __slots__ = ("statement", "_ambiguous_table_name_map") - - plugins: Dict[Tuple[str, str], Type[CompileState]] = {} - - _ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] - - @classmethod - def create_for_statement(cls, statement, compiler, **kw): - # factory construction. - - if statement._propagate_attrs: - plugin_name = statement._propagate_attrs.get( - "compile_state_plugin", "default" - ) - klass = cls.plugins.get( - (plugin_name, statement._effective_plugin_target), None - ) - if klass is None: - klass = cls.plugins[ - ("default", statement._effective_plugin_target) - ] - - else: - klass = cls.plugins[ - ("default", statement._effective_plugin_target) - ] - - if klass is cls: - return cls(statement, compiler, **kw) - else: - return klass.create_for_statement(statement, compiler, **kw) - - def __init__(self, statement, compiler, **kw): - self.statement = statement - - @classmethod - def get_plugin_class( - cls, statement: Executable - ) -> Optional[Type[CompileState]]: - plugin_name = statement._propagate_attrs.get( - "compile_state_plugin", None - ) - - if plugin_name: - key = (plugin_name, statement._effective_plugin_target) - if key in cls.plugins: - return cls.plugins[key] - - # there's no case where we call upon get_plugin_class() and want - # to get None back, there should always be a default. return that - # if there was no plugin-specific class (e.g. Insert with "orm" - # plugin) - try: - return cls.plugins[("default", statement._effective_plugin_target)] - except KeyError: - return None - - @classmethod - def _get_plugin_class_for_plugin( - cls, statement: Executable, plugin_name: str - ) -> Optional[Type[CompileState]]: - try: - return cls.plugins[ - (plugin_name, statement._effective_plugin_target) - ] - except KeyError: - return None - - @classmethod - def plugin_for( - cls, plugin_name: str, visit_name: str - ) -> Callable[[_Fn], _Fn]: - def decorate(cls_to_decorate): - cls.plugins[(plugin_name, visit_name)] = cls_to_decorate - return cls_to_decorate - - return decorate - - -class Generative(HasMemoized): - """Provide a method-chaining pattern in conjunction with the - @_generative decorator.""" - - def _generate(self) -> Self: - skip = self._memoized_keys - cls = self.__class__ - s = cls.__new__(cls) - if skip: - # ensure this iteration remains atomic - s.__dict__ = { - k: v for k, v in self.__dict__.copy().items() if k not in skip - } - else: - s.__dict__ = self.__dict__.copy() - return s - - -class InPlaceGenerative(HasMemoized): - """Provide a method-chaining pattern in conjunction with the - @_generative decorator that mutates in place.""" - - __slots__ = () - - def _generate(self): - skip = self._memoized_keys - # note __dict__ needs to be in __slots__ if this is used - for k in skip: - self.__dict__.pop(k, None) - return self - - -class HasCompileState(Generative): - """A class that has a :class:`.CompileState` associated with it.""" - - _compile_state_plugin: Optional[Type[CompileState]] = None - - _attributes: util.immutabledict[str, Any] = util.EMPTY_DICT - - _compile_state_factory = CompileState.create_for_statement - - -class _MetaOptions(type): - """metaclass for the Options class. - - This metaclass is actually necessary despite the availability of the - ``__init_subclass__()`` hook as this type also provides custom class-level - behavior for the ``__add__()`` method. - - """ - - _cache_attrs: Tuple[str, ...] - - def __add__(self, other): - o1 = self() - - if set(other).difference(self._cache_attrs): - raise TypeError( - "dictionary contains attributes not covered by " - "Options class %s: %r" - % (self, set(other).difference(self._cache_attrs)) - ) - - o1.__dict__.update(other) - return o1 - - if TYPE_CHECKING: - - def __getattr__(self, key: str) -> Any: ... - - def __setattr__(self, key: str, value: Any) -> None: ... - - def __delattr__(self, key: str) -> None: ... - - -class Options(metaclass=_MetaOptions): - """A cacheable option dictionary with defaults.""" - - __slots__ = () - - _cache_attrs: Tuple[str, ...] - - def __init_subclass__(cls) -> None: - dict_ = cls.__dict__ - cls._cache_attrs = tuple( - sorted( - d - for d in dict_ - if not d.startswith("__") - and d not in ("_cache_key_traversal",) - ) - ) - super().__init_subclass__() - - def __init__(self, **kw): - self.__dict__.update(kw) - - def __add__(self, other): - o1 = self.__class__.__new__(self.__class__) - o1.__dict__.update(self.__dict__) - - if set(other).difference(self._cache_attrs): - raise TypeError( - "dictionary contains attributes not covered by " - "Options class %s: %r" - % (self, set(other).difference(self._cache_attrs)) - ) - - o1.__dict__.update(other) - return o1 - - def __eq__(self, other): - # TODO: very inefficient. This is used only in test suites - # right now. - for a, b in zip_longest(self._cache_attrs, other._cache_attrs): - if getattr(self, a) != getattr(other, b): - return False - return True - - def __repr__(self): - # TODO: fairly inefficient, used only in debugging right now. - - return "%s(%s)" % ( - self.__class__.__name__, - ", ".join( - "%s=%r" % (k, self.__dict__[k]) - for k in self._cache_attrs - if k in self.__dict__ - ), - ) - - @classmethod - def isinstance(cls, klass: Type[Any]) -> bool: - return issubclass(cls, klass) - - @hybridmethod - def add_to_element(self, name, value): - return self + {name: getattr(self, name) + value} - - @hybridmethod - def _state_dict_inst(self) -> Mapping[str, Any]: - return self.__dict__ - - _state_dict_const: util.immutabledict[str, Any] = util.EMPTY_DICT - - @_state_dict_inst.classlevel - def _state_dict(cls) -> Mapping[str, Any]: - return cls._state_dict_const - - @classmethod - def safe_merge(cls, other): - d = other._state_dict() - - # only support a merge with another object of our class - # and which does not have attrs that we don't. otherwise - # we risk having state that might not be part of our cache - # key strategy - - if ( - cls is not other.__class__ - and other._cache_attrs - and set(other._cache_attrs).difference(cls._cache_attrs) - ): - raise TypeError( - "other element %r is not empty, is not of type %s, " - "and contains attributes not covered here %r" - % ( - other, - cls, - set(other._cache_attrs).difference(cls._cache_attrs), - ) - ) - return cls + d - - @classmethod - def from_execution_options( - cls, key, attrs, exec_options, statement_exec_options - ): - """process Options argument in terms of execution options. - - - e.g.:: - - ( - load_options, - execution_options, - ) = QueryContext.default_load_options.from_execution_options( - "_sa_orm_load_options", - { - "populate_existing", - "autoflush", - "yield_per" - }, - execution_options, - statement._execution_options, - ) - - get back the Options and refresh "_sa_orm_load_options" in the - exec options dict w/ the Options as well - - """ - - # common case is that no options we are looking for are - # in either dictionary, so cancel for that first - check_argnames = attrs.intersection( - set(exec_options).union(statement_exec_options) - ) - - existing_options = exec_options.get(key, cls) - - if check_argnames: - result = {} - for argname in check_argnames: - local = "_" + argname - if argname in exec_options: - result[local] = exec_options[argname] - elif argname in statement_exec_options: - result[local] = statement_exec_options[argname] - - new_options = existing_options + result - exec_options = util.immutabledict().merge_with( - exec_options, {key: new_options} - ) - return new_options, exec_options - - else: - return existing_options, exec_options - - if TYPE_CHECKING: - - def __getattr__(self, key: str) -> Any: ... - - def __setattr__(self, key: str, value: Any) -> None: ... - - def __delattr__(self, key: str) -> None: ... - - -class CacheableOptions(Options, HasCacheKey): - __slots__ = () - - @hybridmethod - def _gen_cache_key_inst(self, anon_map, bindparams): - return HasCacheKey._gen_cache_key(self, anon_map, bindparams) - - @_gen_cache_key_inst.classlevel - def _gen_cache_key(cls, anon_map, bindparams): - return (cls, ()) - - @hybridmethod - def _generate_cache_key(self): - return HasCacheKey._generate_cache_key_for_object(self) - - -class ExecutableOption(HasCopyInternals): - __slots__ = () - - _annotations = util.EMPTY_DICT - - __visit_name__ = "executable_option" - - _is_has_cache_key = False - - _is_core = True - - def _clone(self, **kw): - """Create a shallow copy of this ExecutableOption.""" - c = self.__class__.__new__(self.__class__) - c.__dict__ = dict(self.__dict__) # type: ignore - return c - - -class Executable(roles.StatementRole): - """Mark a :class:`_expression.ClauseElement` as supporting execution. - - :class:`.Executable` is a superclass for all "statement" types - of objects, including :func:`select`, :func:`delete`, :func:`update`, - :func:`insert`, :func:`text`. - - """ - - supports_execution: bool = True - _execution_options: _ImmutableExecuteOptions = util.EMPTY_DICT - _is_default_generator = False - _with_options: Tuple[ExecutableOption, ...] = () - _with_context_options: Tuple[ - Tuple[Callable[[CompileState], None], Any], ... - ] = () - _compile_options: Optional[Union[Type[CacheableOptions], CacheableOptions]] - - _executable_traverse_internals = [ - ("_with_options", InternalTraversal.dp_executable_options), - ( - "_with_context_options", - ExtendedInternalTraversal.dp_with_context_options, - ), - ("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs), - ] - - is_select = False - is_update = False - is_insert = False - is_text = False - is_delete = False - is_dml = False - - if TYPE_CHECKING: - __visit_name__: str - - def _compile_w_cache( - self, - dialect: Dialect, - *, - compiled_cache: Optional[CompiledCacheType], - column_keys: List[str], - for_executemany: bool = False, - schema_translate_map: Optional[SchemaTranslateMapType] = None, - **kw: Any, - ) -> Tuple[ - Compiled, Optional[Sequence[BindParameter[Any]]], CacheStats - ]: ... - - def _execute_on_connection( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> CursorResult[Any]: ... - - def _execute_on_scalar( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> Any: ... - - @util.ro_non_memoized_property - def _all_selected_columns(self): - raise NotImplementedError() - - @property - def _effective_plugin_target(self) -> str: - return self.__visit_name__ - - @_generative - def options(self, *options: ExecutableOption) -> Self: - """Apply options to this statement. - - In the general sense, options are any kind of Python object - that can be interpreted by the SQL compiler for the statement. - These options can be consumed by specific dialects or specific kinds - of compilers. - - The most commonly known kind of option are the ORM level options - that apply "eager load" and other loading behaviors to an ORM - query. However, options can theoretically be used for many other - purposes. - - For background on specific kinds of options for specific kinds of - statements, refer to the documentation for those option objects. - - .. versionchanged:: 1.4 - added :meth:`.Executable.options` to - Core statement objects towards the goal of allowing unified - Core / ORM querying capabilities. - - .. seealso:: - - :ref:`loading_columns` - refers to options specific to the usage - of ORM queries - - :ref:`relationship_loader_options` - refers to options specific - to the usage of ORM queries - - """ - self._with_options += tuple( - coercions.expect(roles.ExecutableOptionRole, opt) - for opt in options - ) - return self - - @_generative - def _set_compile_options(self, compile_options: CacheableOptions) -> Self: - """Assign the compile options to a new value. - - :param compile_options: appropriate CacheableOptions structure - - """ - - self._compile_options = compile_options - return self - - @_generative - def _update_compile_options(self, options: CacheableOptions) -> Self: - """update the _compile_options with new keys.""" - - assert self._compile_options is not None - self._compile_options += options - return self - - @_generative - def _add_context_option( - self, - callable_: Callable[[CompileState], None], - cache_args: Any, - ) -> Self: - """Add a context option to this statement. - - These are callable functions that will - be given the CompileState object upon compilation. - - A second argument cache_args is required, which will be combined with - the ``__code__`` identity of the function itself in order to produce a - cache key. - - """ - self._with_context_options += ((callable_, cache_args),) - return self - - @overload - def execution_options( - self, - *, - compiled_cache: Optional[CompiledCacheType] = ..., - logging_token: str = ..., - isolation_level: IsolationLevel = ..., - no_parameters: bool = False, - stream_results: bool = False, - max_row_buffer: int = ..., - yield_per: int = ..., - insertmanyvalues_page_size: int = ..., - schema_translate_map: Optional[SchemaTranslateMapType] = ..., - populate_existing: bool = False, - autoflush: bool = False, - synchronize_session: SynchronizeSessionArgument = ..., - dml_strategy: DMLStrategyArgument = ..., - render_nulls: bool = ..., - is_delete_using: bool = ..., - is_update_from: bool = ..., - preserve_rowcount: bool = False, - **opt: Any, - ) -> Self: ... - - @overload - def execution_options(self, **opt: Any) -> Self: ... - - @_generative - def execution_options(self, **kw: Any) -> Self: - """Set non-SQL options for the statement which take effect during - execution. - - Execution options can be set at many scopes, including per-statement, - per-connection, or per execution, using methods such as - :meth:`_engine.Connection.execution_options` and parameters which - accept a dictionary of options such as - :paramref:`_engine.Connection.execute.execution_options` and - :paramref:`_orm.Session.execute.execution_options`. - - The primary characteristic of an execution option, as opposed to - other kinds of options such as ORM loader options, is that - **execution options never affect the compiled SQL of a query, only - things that affect how the SQL statement itself is invoked or how - results are fetched**. That is, execution options are not part of - what's accommodated by SQL compilation nor are they considered part of - the cached state of a statement. - - The :meth:`_sql.Executable.execution_options` method is - :term:`generative`, as - is the case for the method as applied to the :class:`_engine.Engine` - and :class:`_orm.Query` objects, which means when the method is called, - a copy of the object is returned, which applies the given parameters to - that new copy, but leaves the original unchanged:: - - statement = select(table.c.x, table.c.y) - new_statement = statement.execution_options(my_option=True) - - An exception to this behavior is the :class:`_engine.Connection` - object, where the :meth:`_engine.Connection.execution_options` method - is explicitly **not** generative. - - The kinds of options that may be passed to - :meth:`_sql.Executable.execution_options` and other related methods and - parameter dictionaries include parameters that are explicitly consumed - by SQLAlchemy Core or ORM, as well as arbitrary keyword arguments not - defined by SQLAlchemy, which means the methods and/or parameter - dictionaries may be used for user-defined parameters that interact with - custom code, which may access the parameters using methods such as - :meth:`_sql.Executable.get_execution_options` and - :meth:`_engine.Connection.get_execution_options`, or within selected - event hooks using a dedicated ``execution_options`` event parameter - such as - :paramref:`_events.ConnectionEvents.before_execute.execution_options` - or :attr:`_orm.ORMExecuteState.execution_options`, e.g.:: - - from sqlalchemy import event - - @event.listens_for(some_engine, "before_execute") - def _process_opt(conn, statement, multiparams, params, execution_options): - "run a SQL function before invoking a statement" - - if execution_options.get("do_special_thing", False): - conn.exec_driver_sql("run_special_function()") - - Within the scope of options that are explicitly recognized by - SQLAlchemy, most apply to specific classes of objects and not others. - The most common execution options include: - - * :paramref:`_engine.Connection.execution_options.isolation_level` - - sets the isolation level for a connection or a class of connections - via an :class:`_engine.Engine`. This option is accepted only - by :class:`_engine.Connection` or :class:`_engine.Engine`. - - * :paramref:`_engine.Connection.execution_options.stream_results` - - indicates results should be fetched using a server side cursor; - this option is accepted by :class:`_engine.Connection`, by the - :paramref:`_engine.Connection.execute.execution_options` parameter - on :meth:`_engine.Connection.execute`, and additionally by - :meth:`_sql.Executable.execution_options` on a SQL statement object, - as well as by ORM constructs like :meth:`_orm.Session.execute`. - - * :paramref:`_engine.Connection.execution_options.compiled_cache` - - indicates a dictionary that will serve as the - :ref:`SQL compilation cache <sql_caching>` - for a :class:`_engine.Connection` or :class:`_engine.Engine`, as - well as for ORM methods like :meth:`_orm.Session.execute`. - Can be passed as ``None`` to disable caching for statements. - This option is not accepted by - :meth:`_sql.Executable.execution_options` as it is inadvisable to - carry along a compilation cache within a statement object. - - * :paramref:`_engine.Connection.execution_options.schema_translate_map` - - a mapping of schema names used by the - :ref:`Schema Translate Map <schema_translating>` feature, accepted - by :class:`_engine.Connection`, :class:`_engine.Engine`, - :class:`_sql.Executable`, as well as by ORM constructs - like :meth:`_orm.Session.execute`. - - .. seealso:: - - :meth:`_engine.Connection.execution_options` - - :paramref:`_engine.Connection.execute.execution_options` - - :paramref:`_orm.Session.execute.execution_options` - - :ref:`orm_queryguide_execution_options` - documentation on all - ORM-specific execution options - - """ # noqa: E501 - if "isolation_level" in kw: - raise exc.ArgumentError( - "'isolation_level' execution option may only be specified " - "on Connection.execution_options(), or " - "per-engine using the isolation_level " - "argument to create_engine()." - ) - if "compiled_cache" in kw: - raise exc.ArgumentError( - "'compiled_cache' execution option may only be specified " - "on Connection.execution_options(), not per statement." - ) - self._execution_options = self._execution_options.union(kw) - return self - - def get_execution_options(self) -> _ExecuteOptions: - """Get the non-SQL options which will take effect during execution. - - .. versionadded:: 1.3 - - .. seealso:: - - :meth:`.Executable.execution_options` - """ - return self._execution_options - - -class SchemaEventTarget(event.EventTarget): - """Base class for elements that are the targets of :class:`.DDLEvents` - events. - - This includes :class:`.SchemaItem` as well as :class:`.SchemaType`. - - """ - - dispatch: dispatcher[SchemaEventTarget] - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - """Associate with this SchemaEvent's parent object.""" - - def _set_parent_with_dispatch( - self, parent: SchemaEventTarget, **kw: Any - ) -> None: - self.dispatch.before_parent_attach(self, parent) - self._set_parent(parent, **kw) - self.dispatch.after_parent_attach(self, parent) - - -class SchemaVisitor(ClauseVisitor): - """Define the visiting for ``SchemaItem`` objects.""" - - __traverse_options__ = {"schema_visitor": True} - - -class _SentinelDefaultCharacterization(Enum): - NONE = "none" - UNKNOWN = "unknown" - CLIENTSIDE = "clientside" - SENTINEL_DEFAULT = "sentinel_default" - SERVERSIDE = "serverside" - IDENTITY = "identity" - SEQUENCE = "sequence" - - -class _SentinelColumnCharacterization(NamedTuple): - columns: Optional[Sequence[Column[Any]]] = None - is_explicit: bool = False - is_autoinc: bool = False - default_characterization: _SentinelDefaultCharacterization = ( - _SentinelDefaultCharacterization.NONE - ) - - -_COLKEY = TypeVar("_COLKEY", Union[None, str], str) - -_COL_co = TypeVar("_COL_co", bound="ColumnElement[Any]", covariant=True) -_COL = TypeVar("_COL", bound="KeyedColumnElement[Any]") - - -class _ColumnMetrics(Generic[_COL_co]): - __slots__ = ("column",) - - column: _COL_co - - def __init__( - self, collection: ColumnCollection[Any, _COL_co], col: _COL_co - ): - self.column = col - - # proxy_index being non-empty means it was initialized. - # so we need to update it - pi = collection._proxy_index - if pi: - for eps_col in col._expanded_proxy_set: - pi[eps_col].add(self) - - def get_expanded_proxy_set(self): - return self.column._expanded_proxy_set - - def dispose(self, collection): - pi = collection._proxy_index - if not pi: - return - for col in self.column._expanded_proxy_set: - colset = pi.get(col, None) - if colset: - colset.discard(self) - if colset is not None and not colset: - del pi[col] - - def embedded( - self, - target_set: Union[ - Set[ColumnElement[Any]], FrozenSet[ColumnElement[Any]] - ], - ) -> bool: - expanded_proxy_set = self.column._expanded_proxy_set - for t in target_set.difference(expanded_proxy_set): - if not expanded_proxy_set.intersection(_expand_cloned([t])): - return False - return True - - -class ColumnCollection(Generic[_COLKEY, _COL_co]): - """Collection of :class:`_expression.ColumnElement` instances, - typically for - :class:`_sql.FromClause` objects. - - The :class:`_sql.ColumnCollection` object is most commonly available - as the :attr:`_schema.Table.c` or :attr:`_schema.Table.columns` collection - on the :class:`_schema.Table` object, introduced at - :ref:`metadata_tables_and_columns`. - - The :class:`_expression.ColumnCollection` has both mapping- and sequence- - like behaviors. A :class:`_expression.ColumnCollection` usually stores - :class:`_schema.Column` objects, which are then accessible both via mapping - style access as well as attribute access style. - - To access :class:`_schema.Column` objects using ordinary attribute-style - access, specify the name like any other object attribute, such as below - a column named ``employee_name`` is accessed:: - - >>> employee_table.c.employee_name - - To access columns that have names with special characters or spaces, - index-style access is used, such as below which illustrates a column named - ``employee ' payment`` is accessed:: - - >>> employee_table.c["employee ' payment"] - - As the :class:`_sql.ColumnCollection` object provides a Python dictionary - interface, common dictionary method names like - :meth:`_sql.ColumnCollection.keys`, :meth:`_sql.ColumnCollection.values`, - and :meth:`_sql.ColumnCollection.items` are available, which means that - database columns that are keyed under these names also need to use indexed - access:: - - >>> employee_table.c["values"] - - - The name for which a :class:`_schema.Column` would be present is normally - that of the :paramref:`_schema.Column.key` parameter. In some contexts, - such as a :class:`_sql.Select` object that uses a label style set - using the :meth:`_sql.Select.set_label_style` method, a column of a certain - key may instead be represented under a particular label name such - as ``tablename_columnname``:: - - >>> from sqlalchemy import select, column, table - >>> from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL - >>> t = table("t", column("c")) - >>> stmt = select(t).set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) - >>> subq = stmt.subquery() - >>> subq.c.t_c - <sqlalchemy.sql.elements.ColumnClause at 0x7f59dcf04fa0; t_c> - - :class:`.ColumnCollection` also indexes the columns in order and allows - them to be accessible by their integer position:: - - >>> cc[0] - Column('x', Integer(), table=None) - >>> cc[1] - Column('y', Integer(), table=None) - - .. versionadded:: 1.4 :class:`_expression.ColumnCollection` - allows integer-based - index access to the collection. - - Iterating the collection yields the column expressions in order:: - - >>> list(cc) - [Column('x', Integer(), table=None), - Column('y', Integer(), table=None)] - - The base :class:`_expression.ColumnCollection` object can store - duplicates, which can - mean either two columns with the same key, in which case the column - returned by key access is **arbitrary**:: - - >>> x1, x2 = Column('x', Integer), Column('x', Integer) - >>> cc = ColumnCollection(columns=[(x1.name, x1), (x2.name, x2)]) - >>> list(cc) - [Column('x', Integer(), table=None), - Column('x', Integer(), table=None)] - >>> cc['x'] is x1 - False - >>> cc['x'] is x2 - True - - Or it can also mean the same column multiple times. These cases are - supported as :class:`_expression.ColumnCollection` - is used to represent the columns in - a SELECT statement which may include duplicates. - - A special subclass :class:`.DedupeColumnCollection` exists which instead - maintains SQLAlchemy's older behavior of not allowing duplicates; this - collection is used for schema level objects like :class:`_schema.Table` - and - :class:`.PrimaryKeyConstraint` where this deduping is helpful. The - :class:`.DedupeColumnCollection` class also has additional mutation methods - as the schema constructs have more use cases that require removal and - replacement of columns. - - .. versionchanged:: 1.4 :class:`_expression.ColumnCollection` - now stores duplicate - column keys as well as the same column in multiple positions. The - :class:`.DedupeColumnCollection` class is added to maintain the - former behavior in those cases where deduplication as well as - additional replace/remove operations are needed. - - - """ - - __slots__ = "_collection", "_index", "_colset", "_proxy_index" - - _collection: List[Tuple[_COLKEY, _COL_co, _ColumnMetrics[_COL_co]]] - _index: Dict[Union[None, str, int], Tuple[_COLKEY, _COL_co]] - _proxy_index: Dict[ColumnElement[Any], Set[_ColumnMetrics[_COL_co]]] - _colset: Set[_COL_co] - - def __init__( - self, columns: Optional[Iterable[Tuple[_COLKEY, _COL_co]]] = None - ): - object.__setattr__(self, "_colset", set()) - object.__setattr__(self, "_index", {}) - object.__setattr__( - self, "_proxy_index", collections.defaultdict(util.OrderedSet) - ) - object.__setattr__(self, "_collection", []) - if columns: - self._initial_populate(columns) - - @util.preload_module("sqlalchemy.sql.elements") - def __clause_element__(self) -> ClauseList: - elements = util.preloaded.sql_elements - - return elements.ClauseList( - _literal_as_text_role=roles.ColumnsClauseRole, - group=False, - *self._all_columns, - ) - - def _initial_populate( - self, iter_: Iterable[Tuple[_COLKEY, _COL_co]] - ) -> None: - self._populate_separate_keys(iter_) - - @property - def _all_columns(self) -> List[_COL_co]: - return [col for (_, col, _) in self._collection] - - def keys(self) -> List[_COLKEY]: - """Return a sequence of string key names for all columns in this - collection.""" - return [k for (k, _, _) in self._collection] - - def values(self) -> List[_COL_co]: - """Return a sequence of :class:`_sql.ColumnClause` or - :class:`_schema.Column` objects for all columns in this - collection.""" - return [col for (_, col, _) in self._collection] - - def items(self) -> List[Tuple[_COLKEY, _COL_co]]: - """Return a sequence of (key, column) tuples for all columns in this - collection each consisting of a string key name and a - :class:`_sql.ColumnClause` or - :class:`_schema.Column` object. - """ - - return [(k, col) for (k, col, _) in self._collection] - - def __bool__(self) -> bool: - return bool(self._collection) - - def __len__(self) -> int: - return len(self._collection) - - def __iter__(self) -> Iterator[_COL_co]: - # turn to a list first to maintain over a course of changes - return iter([col for _, col, _ in self._collection]) - - @overload - def __getitem__(self, key: Union[str, int]) -> _COL_co: ... - - @overload - def __getitem__( - self, key: Tuple[Union[str, int], ...] - ) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]: ... - - @overload - def __getitem__( - self, key: slice - ) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]: ... - - def __getitem__( - self, key: Union[str, int, slice, Tuple[Union[str, int], ...]] - ) -> Union[ReadOnlyColumnCollection[_COLKEY, _COL_co], _COL_co]: - try: - if isinstance(key, (tuple, slice)): - if isinstance(key, slice): - cols = ( - (sub_key, col) - for (sub_key, col, _) in self._collection[key] - ) - else: - cols = (self._index[sub_key] for sub_key in key) - - return ColumnCollection(cols).as_readonly() - else: - return self._index[key][1] - except KeyError as err: - if isinstance(err.args[0], int): - raise IndexError(err.args[0]) from err - else: - raise - - def __getattr__(self, key: str) -> _COL_co: - try: - return self._index[key][1] - except KeyError as err: - raise AttributeError(key) from err - - def __contains__(self, key: str) -> bool: - if key not in self._index: - if not isinstance(key, str): - raise exc.ArgumentError( - "__contains__ requires a string argument" - ) - return False - else: - return True - - def compare(self, other: ColumnCollection[Any, Any]) -> bool: - """Compare this :class:`_expression.ColumnCollection` to another - based on the names of the keys""" - - for l, r in zip_longest(self, other): - if l is not r: - return False - else: - return True - - def __eq__(self, other: Any) -> bool: - return self.compare(other) - - def get( - self, key: str, default: Optional[_COL_co] = None - ) -> Optional[_COL_co]: - """Get a :class:`_sql.ColumnClause` or :class:`_schema.Column` object - based on a string key name from this - :class:`_expression.ColumnCollection`.""" - - if key in self._index: - return self._index[key][1] - else: - return default - - def __str__(self) -> str: - return "%s(%s)" % ( - self.__class__.__name__, - ", ".join(str(c) for c in self), - ) - - def __setitem__(self, key: str, value: Any) -> NoReturn: - raise NotImplementedError() - - def __delitem__(self, key: str) -> NoReturn: - raise NotImplementedError() - - def __setattr__(self, key: str, obj: Any) -> NoReturn: - raise NotImplementedError() - - def clear(self) -> NoReturn: - """Dictionary clear() is not implemented for - :class:`_sql.ColumnCollection`.""" - raise NotImplementedError() - - def remove(self, column: Any) -> None: - raise NotImplementedError() - - def update(self, iter_: Any) -> NoReturn: - """Dictionary update() is not implemented for - :class:`_sql.ColumnCollection`.""" - raise NotImplementedError() - - # https://github.com/python/mypy/issues/4266 - __hash__ = None # type: ignore - - def _populate_separate_keys( - self, iter_: Iterable[Tuple[_COLKEY, _COL_co]] - ) -> None: - """populate from an iterator of (key, column)""" - - self._collection[:] = collection = [ - (k, c, _ColumnMetrics(self, c)) for k, c in iter_ - ] - self._colset.update(c._deannotate() for _, c, _ in collection) - self._index.update( - {idx: (k, c) for idx, (k, c, _) in enumerate(collection)} - ) - self._index.update({k: (k, col) for k, col, _ in reversed(collection)}) - - def add( - self, column: ColumnElement[Any], key: Optional[_COLKEY] = None - ) -> None: - """Add a column to this :class:`_sql.ColumnCollection`. - - .. note:: - - This method is **not normally used by user-facing code**, as the - :class:`_sql.ColumnCollection` is usually part of an existing - object such as a :class:`_schema.Table`. To add a - :class:`_schema.Column` to an existing :class:`_schema.Table` - object, use the :meth:`_schema.Table.append_column` method. - - """ - colkey: _COLKEY - - if key is None: - colkey = column.key # type: ignore - else: - colkey = key - - l = len(self._collection) - - # don't really know how this part is supposed to work w/ the - # covariant thing - - _column = cast(_COL_co, column) - - self._collection.append( - (colkey, _column, _ColumnMetrics(self, _column)) - ) - self._colset.add(_column._deannotate()) - self._index[l] = (colkey, _column) - if colkey not in self._index: - self._index[colkey] = (colkey, _column) - - def __getstate__(self) -> Dict[str, Any]: - return { - "_collection": [(k, c) for k, c, _ in self._collection], - "_index": self._index, - } - - def __setstate__(self, state: Dict[str, Any]) -> None: - object.__setattr__(self, "_index", state["_index"]) - object.__setattr__( - self, "_proxy_index", collections.defaultdict(util.OrderedSet) - ) - object.__setattr__( - self, - "_collection", - [ - (k, c, _ColumnMetrics(self, c)) - for (k, c) in state["_collection"] - ], - ) - object.__setattr__( - self, "_colset", {col for k, col, _ in self._collection} - ) - - def contains_column(self, col: ColumnElement[Any]) -> bool: - """Checks if a column object exists in this collection""" - if col not in self._colset: - if isinstance(col, str): - raise exc.ArgumentError( - "contains_column cannot be used with string arguments. " - "Use ``col_name in table.c`` instead." - ) - return False - else: - return True - - def as_readonly(self) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]: - """Return a "read only" form of this - :class:`_sql.ColumnCollection`.""" - - return ReadOnlyColumnCollection(self) - - def _init_proxy_index(self): - """populate the "proxy index", if empty. - - proxy index is added in 2.0 to provide more efficient operation - for the corresponding_column() method. - - For reasons of both time to construct new .c collections as well as - memory conservation for large numbers of large .c collections, the - proxy_index is only filled if corresponding_column() is called. once - filled it stays that way, and new _ColumnMetrics objects created after - that point will populate it with new data. Note this case would be - unusual, if not nonexistent, as it means a .c collection is being - mutated after corresponding_column() were used, however it is tested in - test/base/test_utils.py. - - """ - pi = self._proxy_index - if pi: - return - - for _, _, metrics in self._collection: - eps = metrics.column._expanded_proxy_set - - for eps_col in eps: - pi[eps_col].add(metrics) - - def corresponding_column( - self, column: _COL, require_embedded: bool = False - ) -> Optional[Union[_COL, _COL_co]]: - """Given a :class:`_expression.ColumnElement`, return the exported - :class:`_expression.ColumnElement` object from this - :class:`_expression.ColumnCollection` - which corresponds to that original :class:`_expression.ColumnElement` - via a common - ancestor column. - - :param column: the target :class:`_expression.ColumnElement` - to be matched. - - :param require_embedded: only return corresponding columns for - the given :class:`_expression.ColumnElement`, if the given - :class:`_expression.ColumnElement` - is actually present within a sub-element - of this :class:`_expression.Selectable`. - Normally the column will match if - it merely shares a common ancestor with one of the exported - columns of this :class:`_expression.Selectable`. - - .. seealso:: - - :meth:`_expression.Selectable.corresponding_column` - - invokes this method - against the collection returned by - :attr:`_expression.Selectable.exported_columns`. - - .. versionchanged:: 1.4 the implementation for ``corresponding_column`` - was moved onto the :class:`_expression.ColumnCollection` itself. - - """ - # TODO: cython candidate - - # don't dig around if the column is locally present - if column in self._colset: - return column - - selected_intersection, selected_metrics = None, None - target_set = column.proxy_set - - pi = self._proxy_index - if not pi: - self._init_proxy_index() - - for current_metrics in ( - mm for ts in target_set if ts in pi for mm in pi[ts] - ): - if not require_embedded or current_metrics.embedded(target_set): - if selected_metrics is None: - # no corresponding column yet, pick this one. - selected_metrics = current_metrics - continue - - current_intersection = target_set.intersection( - current_metrics.column._expanded_proxy_set - ) - if selected_intersection is None: - selected_intersection = target_set.intersection( - selected_metrics.column._expanded_proxy_set - ) - - if len(current_intersection) > len(selected_intersection): - # 'current' has a larger field of correspondence than - # 'selected'. i.e. selectable.c.a1_x->a1.c.x->table.c.x - # matches a1.c.x->table.c.x better than - # selectable.c.x->table.c.x does. - - selected_metrics = current_metrics - selected_intersection = current_intersection - elif current_intersection == selected_intersection: - # they have the same field of correspondence. see - # which proxy_set has fewer columns in it, which - # indicates a closer relationship with the root - # column. Also take into account the "weight" - # attribute which CompoundSelect() uses to give - # higher precedence to columns based on vertical - # position in the compound statement, and discard - # columns that have no reference to the target - # column (also occurs with CompoundSelect) - - selected_col_distance = sum( - [ - sc._annotations.get("weight", 1) - for sc in ( - selected_metrics.column._uncached_proxy_list() - ) - if sc.shares_lineage(column) - ], - ) - current_col_distance = sum( - [ - sc._annotations.get("weight", 1) - for sc in ( - current_metrics.column._uncached_proxy_list() - ) - if sc.shares_lineage(column) - ], - ) - if current_col_distance < selected_col_distance: - selected_metrics = current_metrics - selected_intersection = current_intersection - - return selected_metrics.column if selected_metrics else None - - -_NAMEDCOL = TypeVar("_NAMEDCOL", bound="NamedColumn[Any]") - - -class DedupeColumnCollection(ColumnCollection[str, _NAMEDCOL]): - """A :class:`_expression.ColumnCollection` - that maintains deduplicating behavior. - - This is useful by schema level objects such as :class:`_schema.Table` and - :class:`.PrimaryKeyConstraint`. The collection includes more - sophisticated mutator methods as well to suit schema objects which - require mutable column collections. - - .. versionadded:: 1.4 - - """ - - def add( - self, column: ColumnElement[Any], key: Optional[str] = None - ) -> None: - named_column = cast(_NAMEDCOL, column) - if key is not None and named_column.key != key: - raise exc.ArgumentError( - "DedupeColumnCollection requires columns be under " - "the same key as their .key" - ) - key = named_column.key - - if key is None: - raise exc.ArgumentError( - "Can't add unnamed column to column collection" - ) - - if key in self._index: - existing = self._index[key][1] - - if existing is named_column: - return - - self.replace(named_column) - - # pop out memoized proxy_set as this - # operation may very well be occurring - # in a _make_proxy operation - util.memoized_property.reset(named_column, "proxy_set") - else: - self._append_new_column(key, named_column) - - def _append_new_column(self, key: str, named_column: _NAMEDCOL) -> None: - l = len(self._collection) - self._collection.append( - (key, named_column, _ColumnMetrics(self, named_column)) - ) - self._colset.add(named_column._deannotate()) - self._index[l] = (key, named_column) - self._index[key] = (key, named_column) - - def _populate_separate_keys( - self, iter_: Iterable[Tuple[str, _NAMEDCOL]] - ) -> None: - """populate from an iterator of (key, column)""" - cols = list(iter_) - - replace_col = [] - for k, col in cols: - if col.key != k: - raise exc.ArgumentError( - "DedupeColumnCollection requires columns be under " - "the same key as their .key" - ) - if col.name in self._index and col.key != col.name: - replace_col.append(col) - elif col.key in self._index: - replace_col.append(col) - else: - self._index[k] = (k, col) - self._collection.append((k, col, _ColumnMetrics(self, col))) - self._colset.update(c._deannotate() for (k, c, _) in self._collection) - - self._index.update( - (idx, (k, c)) for idx, (k, c, _) in enumerate(self._collection) - ) - for col in replace_col: - self.replace(col) - - def extend(self, iter_: Iterable[_NAMEDCOL]) -> None: - self._populate_separate_keys((col.key, col) for col in iter_) - - def remove(self, column: _NAMEDCOL) -> None: - if column not in self._colset: - raise ValueError( - "Can't remove column %r; column is not in this collection" - % column - ) - del self._index[column.key] - self._colset.remove(column) - self._collection[:] = [ - (k, c, metrics) - for (k, c, metrics) in self._collection - if c is not column - ] - for metrics in self._proxy_index.get(column, ()): - metrics.dispose(self) - - self._index.update( - {idx: (k, col) for idx, (k, col, _) in enumerate(self._collection)} - ) - # delete higher index - del self._index[len(self._collection)] - - def replace( - self, - column: _NAMEDCOL, - extra_remove: Optional[Iterable[_NAMEDCOL]] = None, - ) -> None: - """add the given column to this collection, removing unaliased - versions of this column as well as existing columns with the - same key. - - e.g.:: - - t = Table('sometable', metadata, Column('col1', Integer)) - t.columns.replace(Column('col1', Integer, key='columnone')) - - will remove the original 'col1' from the collection, and add - the new column under the name 'columnname'. - - Used by schema.Column to override columns during table reflection. - - """ - - if extra_remove: - remove_col = set(extra_remove) - else: - remove_col = set() - # remove up to two columns based on matches of name as well as key - if column.name in self._index and column.key != column.name: - other = self._index[column.name][1] - if other.name == other.key: - remove_col.add(other) - - if column.key in self._index: - remove_col.add(self._index[column.key][1]) - - if not remove_col: - self._append_new_column(column.key, column) - return - new_cols: List[Tuple[str, _NAMEDCOL, _ColumnMetrics[_NAMEDCOL]]] = [] - replaced = False - for k, col, metrics in self._collection: - if col in remove_col: - if not replaced: - replaced = True - new_cols.append( - (column.key, column, _ColumnMetrics(self, column)) - ) - else: - new_cols.append((k, col, metrics)) - - if remove_col: - self._colset.difference_update(remove_col) - - for rc in remove_col: - for metrics in self._proxy_index.get(rc, ()): - metrics.dispose(self) - - if not replaced: - new_cols.append((column.key, column, _ColumnMetrics(self, column))) - - self._colset.add(column._deannotate()) - self._collection[:] = new_cols - - self._index.clear() - - self._index.update( - {idx: (k, col) for idx, (k, col, _) in enumerate(self._collection)} - ) - self._index.update({k: (k, col) for (k, col, _) in self._collection}) - - -class ReadOnlyColumnCollection( - util.ReadOnlyContainer, ColumnCollection[_COLKEY, _COL_co] -): - __slots__ = ("_parent",) - - def __init__(self, collection): - object.__setattr__(self, "_parent", collection) - object.__setattr__(self, "_colset", collection._colset) - object.__setattr__(self, "_index", collection._index) - object.__setattr__(self, "_collection", collection._collection) - object.__setattr__(self, "_proxy_index", collection._proxy_index) - - def __getstate__(self): - return {"_parent": self._parent} - - def __setstate__(self, state): - parent = state["_parent"] - self.__init__(parent) # type: ignore - - def add(self, column: Any, key: Any = ...) -> Any: - self._readonly() - - def extend(self, elements: Any) -> NoReturn: - self._readonly() - - def remove(self, item: Any) -> NoReturn: - self._readonly() - - -class ColumnSet(util.OrderedSet["ColumnClause[Any]"]): - def contains_column(self, col): - return col in self - - def extend(self, cols): - for col in cols: - self.add(col) - - def __eq__(self, other): - l = [] - for c in other: - for local in self: - if c.shares_lineage(local): - l.append(c == local) - return elements.and_(*l) - - def __hash__(self): - return hash(tuple(x for x in self)) - - -def _entity_namespace( - entity: Union[_HasEntityNamespace, ExternallyTraversible] -) -> _EntityNamespace: - """Return the nearest .entity_namespace for the given entity. - - If not immediately available, does an iterate to find a sub-element - that has one, if any. - - """ - try: - return cast(_HasEntityNamespace, entity).entity_namespace - except AttributeError: - for elem in visitors.iterate(cast(ExternallyTraversible, entity)): - if _is_has_entity_namespace(elem): - return elem.entity_namespace - else: - raise - - -def _entity_namespace_key( - entity: Union[_HasEntityNamespace, ExternallyTraversible], - key: str, - default: Union[SQLCoreOperations[Any], _NoArg] = NO_ARG, -) -> SQLCoreOperations[Any]: - """Return an entry from an entity_namespace. - - - Raises :class:`_exc.InvalidRequestError` rather than attribute error - on not found. - - """ - - try: - ns = _entity_namespace(entity) - if default is not NO_ARG: - return getattr(ns, key, default) - else: - return getattr(ns, key) # type: ignore - except AttributeError as err: - raise exc.InvalidRequestError( - 'Entity namespace for "%s" has no property "%s"' % (entity, key) - ) from err diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/cache_key.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/cache_key.py deleted file mode 100644 index 1172d3c..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/cache_key.py +++ /dev/null @@ -1,1057 +0,0 @@ -# sql/cache_key.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -from __future__ import annotations - -import enum -from itertools import zip_longest -import typing -from typing import Any -from typing import Callable -from typing import Dict -from typing import Iterable -from typing import Iterator -from typing import List -from typing import MutableMapping -from typing import NamedTuple -from typing import Optional -from typing import Sequence -from typing import Tuple -from typing import Union - -from .visitors import anon_map -from .visitors import HasTraversalDispatch -from .visitors import HasTraverseInternals -from .visitors import InternalTraversal -from .visitors import prefix_anon_map -from .. import util -from ..inspection import inspect -from ..util import HasMemoized -from ..util.typing import Literal -from ..util.typing import Protocol - -if typing.TYPE_CHECKING: - from .elements import BindParameter - from .elements import ClauseElement - from .elements import ColumnElement - from .visitors import _TraverseInternalsType - from ..engine.interfaces import _CoreSingleExecuteParams - - -class _CacheKeyTraversalDispatchType(Protocol): - def __call__( - s, self: HasCacheKey, visitor: _CacheKeyTraversal - ) -> _CacheKeyTraversalDispatchTypeReturn: ... - - -class CacheConst(enum.Enum): - NO_CACHE = 0 - - -NO_CACHE = CacheConst.NO_CACHE - - -_CacheKeyTraversalType = Union[ - "_TraverseInternalsType", Literal[CacheConst.NO_CACHE], Literal[None] -] - - -class CacheTraverseTarget(enum.Enum): - CACHE_IN_PLACE = 0 - CALL_GEN_CACHE_KEY = 1 - STATIC_CACHE_KEY = 2 - PROPAGATE_ATTRS = 3 - ANON_NAME = 4 - - -( - CACHE_IN_PLACE, - CALL_GEN_CACHE_KEY, - STATIC_CACHE_KEY, - PROPAGATE_ATTRS, - ANON_NAME, -) = tuple(CacheTraverseTarget) - -_CacheKeyTraversalDispatchTypeReturn = Sequence[ - Tuple[ - str, - Any, - Union[ - Callable[..., Tuple[Any, ...]], - CacheTraverseTarget, - InternalTraversal, - ], - ] -] - - -class HasCacheKey: - """Mixin for objects which can produce a cache key. - - This class is usually in a hierarchy that starts with the - :class:`.HasTraverseInternals` base, but this is optional. Currently, - the class should be able to work on its own without including - :class:`.HasTraverseInternals`. - - .. seealso:: - - :class:`.CacheKey` - - :ref:`sql_caching` - - """ - - __slots__ = () - - _cache_key_traversal: _CacheKeyTraversalType = NO_CACHE - - _is_has_cache_key = True - - _hierarchy_supports_caching = True - """private attribute which may be set to False to prevent the - inherit_cache warning from being emitted for a hierarchy of subclasses. - - Currently applies to the :class:`.ExecutableDDLElement` hierarchy which - does not implement caching. - - """ - - inherit_cache: Optional[bool] = None - """Indicate if this :class:`.HasCacheKey` instance should make use of the - cache key generation scheme used by its immediate superclass. - - The attribute defaults to ``None``, which indicates that a construct has - not yet taken into account whether or not its appropriate for it to - participate in caching; this is functionally equivalent to setting the - value to ``False``, except that a warning is also emitted. - - This flag can be set to ``True`` on a particular class, if the SQL that - corresponds to the object does not change based on attributes which - are local to this class, and not its superclass. - - .. seealso:: - - :ref:`compilerext_caching` - General guideslines for setting the - :attr:`.HasCacheKey.inherit_cache` attribute for third-party or user - defined SQL constructs. - - """ - - __slots__ = () - - _generated_cache_key_traversal: Any - - @classmethod - def _generate_cache_attrs( - cls, - ) -> Union[_CacheKeyTraversalDispatchType, Literal[CacheConst.NO_CACHE]]: - """generate cache key dispatcher for a new class. - - This sets the _generated_cache_key_traversal attribute once called - so should only be called once per class. - - """ - inherit_cache = cls.__dict__.get("inherit_cache", None) - inherit = bool(inherit_cache) - - if inherit: - _cache_key_traversal = getattr(cls, "_cache_key_traversal", None) - if _cache_key_traversal is None: - try: - assert issubclass(cls, HasTraverseInternals) - _cache_key_traversal = cls._traverse_internals - except AttributeError: - cls._generated_cache_key_traversal = NO_CACHE - return NO_CACHE - - assert _cache_key_traversal is not NO_CACHE, ( - f"class {cls} has _cache_key_traversal=NO_CACHE, " - "which conflicts with inherit_cache=True" - ) - - # TODO: wouldn't we instead get this from our superclass? - # also, our superclass may not have this yet, but in any case, - # we'd generate for the superclass that has it. this is a little - # more complicated, so for the moment this is a little less - # efficient on startup but simpler. - return _cache_key_traversal_visitor.generate_dispatch( - cls, - _cache_key_traversal, - "_generated_cache_key_traversal", - ) - else: - _cache_key_traversal = cls.__dict__.get( - "_cache_key_traversal", None - ) - if _cache_key_traversal is None: - _cache_key_traversal = cls.__dict__.get( - "_traverse_internals", None - ) - if _cache_key_traversal is None: - cls._generated_cache_key_traversal = NO_CACHE - if ( - inherit_cache is None - and cls._hierarchy_supports_caching - ): - util.warn( - "Class %s will not make use of SQL compilation " - "caching as it does not set the 'inherit_cache' " - "attribute to ``True``. This can have " - "significant performance implications including " - "some performance degradations in comparison to " - "prior SQLAlchemy versions. Set this attribute " - "to True if this object can make use of the cache " - "key generated by the superclass. Alternatively, " - "this attribute may be set to False which will " - "disable this warning." % (cls.__name__), - code="cprf", - ) - return NO_CACHE - - return _cache_key_traversal_visitor.generate_dispatch( - cls, - _cache_key_traversal, - "_generated_cache_key_traversal", - ) - - @util.preload_module("sqlalchemy.sql.elements") - def _gen_cache_key( - self, anon_map: anon_map, bindparams: List[BindParameter[Any]] - ) -> Optional[Tuple[Any, ...]]: - """return an optional cache key. - - The cache key is a tuple which can contain any series of - objects that are hashable and also identifies - this object uniquely within the presence of a larger SQL expression - or statement, for the purposes of caching the resulting query. - - The cache key should be based on the SQL compiled structure that would - ultimately be produced. That is, two structures that are composed in - exactly the same way should produce the same cache key; any difference - in the structures that would affect the SQL string or the type handlers - should result in a different cache key. - - If a structure cannot produce a useful cache key, the NO_CACHE - symbol should be added to the anon_map and the method should - return None. - - """ - - cls = self.__class__ - - id_, found = anon_map.get_anon(self) - if found: - return (id_, cls) - - dispatcher: Union[ - Literal[CacheConst.NO_CACHE], - _CacheKeyTraversalDispatchType, - ] - - try: - dispatcher = cls.__dict__["_generated_cache_key_traversal"] - except KeyError: - # traversals.py -> _preconfigure_traversals() - # may be used to run these ahead of time, but - # is not enabled right now. - # this block will generate any remaining dispatchers. - dispatcher = cls._generate_cache_attrs() - - if dispatcher is NO_CACHE: - anon_map[NO_CACHE] = True - return None - - result: Tuple[Any, ...] = (id_, cls) - - # inline of _cache_key_traversal_visitor.run_generated_dispatch() - - for attrname, obj, meth in dispatcher( - self, _cache_key_traversal_visitor - ): - if obj is not None: - # TODO: see if C code can help here as Python lacks an - # efficient switch construct - - if meth is STATIC_CACHE_KEY: - sck = obj._static_cache_key - if sck is NO_CACHE: - anon_map[NO_CACHE] = True - return None - result += (attrname, sck) - elif meth is ANON_NAME: - elements = util.preloaded.sql_elements - if isinstance(obj, elements._anonymous_label): - obj = obj.apply_map(anon_map) # type: ignore - result += (attrname, obj) - elif meth is CALL_GEN_CACHE_KEY: - result += ( - attrname, - obj._gen_cache_key(anon_map, bindparams), - ) - - # remaining cache functions are against - # Python tuples, dicts, lists, etc. so we can skip - # if they are empty - elif obj: - if meth is CACHE_IN_PLACE: - result += (attrname, obj) - elif meth is PROPAGATE_ATTRS: - result += ( - attrname, - obj["compile_state_plugin"], - ( - obj["plugin_subject"]._gen_cache_key( - anon_map, bindparams - ) - if obj["plugin_subject"] - else None - ), - ) - elif meth is InternalTraversal.dp_annotations_key: - # obj is here is the _annotations dict. Table uses - # a memoized version of it. however in other cases, - # we generate it given anon_map as we may be from a - # Join, Aliased, etc. - # see #8790 - - if self._gen_static_annotations_cache_key: # type: ignore # noqa: E501 - result += self._annotations_cache_key # type: ignore # noqa: E501 - else: - result += self._gen_annotations_cache_key(anon_map) # type: ignore # noqa: E501 - - elif ( - meth is InternalTraversal.dp_clauseelement_list - or meth is InternalTraversal.dp_clauseelement_tuple - or meth - is InternalTraversal.dp_memoized_select_entities - ): - result += ( - attrname, - tuple( - [ - elem._gen_cache_key(anon_map, bindparams) - for elem in obj - ] - ), - ) - else: - result += meth( # type: ignore - attrname, obj, self, anon_map, bindparams - ) - return result - - def _generate_cache_key(self) -> Optional[CacheKey]: - """return a cache key. - - The cache key is a tuple which can contain any series of - objects that are hashable and also identifies - this object uniquely within the presence of a larger SQL expression - or statement, for the purposes of caching the resulting query. - - The cache key should be based on the SQL compiled structure that would - ultimately be produced. That is, two structures that are composed in - exactly the same way should produce the same cache key; any difference - in the structures that would affect the SQL string or the type handlers - should result in a different cache key. - - The cache key returned by this method is an instance of - :class:`.CacheKey`, which consists of a tuple representing the - cache key, as well as a list of :class:`.BindParameter` objects - which are extracted from the expression. While two expressions - that produce identical cache key tuples will themselves generate - identical SQL strings, the list of :class:`.BindParameter` objects - indicates the bound values which may have different values in - each one; these bound parameters must be consulted in order to - execute the statement with the correct parameters. - - a :class:`_expression.ClauseElement` structure that does not implement - a :meth:`._gen_cache_key` method and does not implement a - :attr:`.traverse_internals` attribute will not be cacheable; when - such an element is embedded into a larger structure, this method - will return None, indicating no cache key is available. - - """ - - bindparams: List[BindParameter[Any]] = [] - - _anon_map = anon_map() - key = self._gen_cache_key(_anon_map, bindparams) - if NO_CACHE in _anon_map: - return None - else: - assert key is not None - return CacheKey(key, bindparams) - - @classmethod - def _generate_cache_key_for_object( - cls, obj: HasCacheKey - ) -> Optional[CacheKey]: - bindparams: List[BindParameter[Any]] = [] - - _anon_map = anon_map() - key = obj._gen_cache_key(_anon_map, bindparams) - if NO_CACHE in _anon_map: - return None - else: - assert key is not None - return CacheKey(key, bindparams) - - -class HasCacheKeyTraverse(HasTraverseInternals, HasCacheKey): - pass - - -class MemoizedHasCacheKey(HasCacheKey, HasMemoized): - __slots__ = () - - @HasMemoized.memoized_instancemethod - def _generate_cache_key(self) -> Optional[CacheKey]: - return HasCacheKey._generate_cache_key(self) - - -class SlotsMemoizedHasCacheKey(HasCacheKey, util.MemoizedSlots): - __slots__ = () - - def _memoized_method__generate_cache_key(self) -> Optional[CacheKey]: - return HasCacheKey._generate_cache_key(self) - - -class CacheKey(NamedTuple): - """The key used to identify a SQL statement construct in the - SQL compilation cache. - - .. seealso:: - - :ref:`sql_caching` - - """ - - key: Tuple[Any, ...] - bindparams: Sequence[BindParameter[Any]] - - # can't set __hash__ attribute because it interferes - # with namedtuple - # can't use "if not TYPE_CHECKING" because mypy rejects it - # inside of a NamedTuple - def __hash__(self) -> Optional[int]: # type: ignore - """CacheKey itself is not hashable - hash the .key portion""" - return None - - def to_offline_string( - self, - statement_cache: MutableMapping[Any, str], - statement: ClauseElement, - parameters: _CoreSingleExecuteParams, - ) -> str: - """Generate an "offline string" form of this :class:`.CacheKey` - - The "offline string" is basically the string SQL for the - statement plus a repr of the bound parameter values in series. - Whereas the :class:`.CacheKey` object is dependent on in-memory - identities in order to work as a cache key, the "offline" version - is suitable for a cache that will work for other processes as well. - - The given ``statement_cache`` is a dictionary-like object where the - string form of the statement itself will be cached. This dictionary - should be in a longer lived scope in order to reduce the time spent - stringifying statements. - - - """ - if self.key not in statement_cache: - statement_cache[self.key] = sql_str = str(statement) - else: - sql_str = statement_cache[self.key] - - if not self.bindparams: - param_tuple = tuple(parameters[key] for key in sorted(parameters)) - else: - param_tuple = tuple( - parameters.get(bindparam.key, bindparam.value) - for bindparam in self.bindparams - ) - - return repr((sql_str, param_tuple)) - - def __eq__(self, other: Any) -> bool: - return bool(self.key == other.key) - - def __ne__(self, other: Any) -> bool: - return not (self.key == other.key) - - @classmethod - def _diff_tuples(cls, left: CacheKey, right: CacheKey) -> str: - ck1 = CacheKey(left, []) - ck2 = CacheKey(right, []) - return ck1._diff(ck2) - - def _whats_different(self, other: CacheKey) -> Iterator[str]: - k1 = self.key - k2 = other.key - - stack: List[int] = [] - pickup_index = 0 - while True: - s1, s2 = k1, k2 - for idx in stack: - s1 = s1[idx] - s2 = s2[idx] - - for idx, (e1, e2) in enumerate(zip_longest(s1, s2)): - if idx < pickup_index: - continue - if e1 != e2: - if isinstance(e1, tuple) and isinstance(e2, tuple): - stack.append(idx) - break - else: - yield "key%s[%d]: %s != %s" % ( - "".join("[%d]" % id_ for id_ in stack), - idx, - e1, - e2, - ) - else: - pickup_index = stack.pop(-1) - break - - def _diff(self, other: CacheKey) -> str: - return ", ".join(self._whats_different(other)) - - def __str__(self) -> str: - stack: List[Union[Tuple[Any, ...], HasCacheKey]] = [self.key] - - output = [] - sentinel = object() - indent = -1 - while stack: - elem = stack.pop(0) - if elem is sentinel: - output.append((" " * (indent * 2)) + "),") - indent -= 1 - elif isinstance(elem, tuple): - if not elem: - output.append((" " * ((indent + 1) * 2)) + "()") - else: - indent += 1 - stack = list(elem) + [sentinel] + stack - output.append((" " * (indent * 2)) + "(") - else: - if isinstance(elem, HasCacheKey): - repr_ = "<%s object at %s>" % ( - type(elem).__name__, - hex(id(elem)), - ) - else: - repr_ = repr(elem) - output.append((" " * (indent * 2)) + " " + repr_ + ", ") - - return "CacheKey(key=%s)" % ("\n".join(output),) - - def _generate_param_dict(self) -> Dict[str, Any]: - """used for testing""" - - _anon_map = prefix_anon_map() - return {b.key % _anon_map: b.effective_value for b in self.bindparams} - - @util.preload_module("sqlalchemy.sql.elements") - def _apply_params_to_element( - self, original_cache_key: CacheKey, target_element: ColumnElement[Any] - ) -> ColumnElement[Any]: - if target_element._is_immutable or original_cache_key is self: - return target_element - - elements = util.preloaded.sql_elements - return elements._OverrideBinds( - target_element, self.bindparams, original_cache_key.bindparams - ) - - -def _ad_hoc_cache_key_from_args( - tokens: Tuple[Any, ...], - traverse_args: Iterable[Tuple[str, InternalTraversal]], - args: Iterable[Any], -) -> Tuple[Any, ...]: - """a quick cache key generator used by reflection.flexi_cache.""" - bindparams: List[BindParameter[Any]] = [] - - _anon_map = anon_map() - - tup = tokens - - for (attrname, sym), arg in zip(traverse_args, args): - key = sym.name - visit_key = key.replace("dp_", "visit_") - - if arg is None: - tup += (attrname, None) - continue - - meth = getattr(_cache_key_traversal_visitor, visit_key) - if meth is CACHE_IN_PLACE: - tup += (attrname, arg) - elif meth in ( - CALL_GEN_CACHE_KEY, - STATIC_CACHE_KEY, - ANON_NAME, - PROPAGATE_ATTRS, - ): - raise NotImplementedError( - f"Haven't implemented symbol {meth} for ad-hoc key from args" - ) - else: - tup += meth(attrname, arg, None, _anon_map, bindparams) - return tup - - -class _CacheKeyTraversal(HasTraversalDispatch): - # very common elements are inlined into the main _get_cache_key() method - # to produce a dramatic savings in Python function call overhead - - visit_has_cache_key = visit_clauseelement = CALL_GEN_CACHE_KEY - visit_clauseelement_list = InternalTraversal.dp_clauseelement_list - visit_annotations_key = InternalTraversal.dp_annotations_key - visit_clauseelement_tuple = InternalTraversal.dp_clauseelement_tuple - visit_memoized_select_entities = ( - InternalTraversal.dp_memoized_select_entities - ) - - visit_string = visit_boolean = visit_operator = visit_plain_obj = ( - CACHE_IN_PLACE - ) - visit_statement_hint_list = CACHE_IN_PLACE - visit_type = STATIC_CACHE_KEY - visit_anon_name = ANON_NAME - - visit_propagate_attrs = PROPAGATE_ATTRS - - def visit_with_context_options( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return tuple((fn.__code__, c_key) for fn, c_key in obj) - - def visit_inspectable( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return (attrname, inspect(obj)._gen_cache_key(anon_map, bindparams)) - - def visit_string_list( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return tuple(obj) - - def visit_multi( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - ( - obj._gen_cache_key(anon_map, bindparams) - if isinstance(obj, HasCacheKey) - else obj - ), - ) - - def visit_multi_list( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - tuple( - ( - elem._gen_cache_key(anon_map, bindparams) - if isinstance(elem, HasCacheKey) - else elem - ) - for elem in obj - ), - ) - - def visit_has_cache_key_tuples( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - return ( - attrname, - tuple( - tuple( - elem._gen_cache_key(anon_map, bindparams) - for elem in tup_elem - ) - for tup_elem in obj - ), - ) - - def visit_has_cache_key_list( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - return ( - attrname, - tuple(elem._gen_cache_key(anon_map, bindparams) for elem in obj), - ) - - def visit_executable_options( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - return ( - attrname, - tuple( - elem._gen_cache_key(anon_map, bindparams) - for elem in obj - if elem._is_has_cache_key - ), - ) - - def visit_inspectable_list( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return self.visit_has_cache_key_list( - attrname, [inspect(o) for o in obj], parent, anon_map, bindparams - ) - - def visit_clauseelement_tuples( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return self.visit_has_cache_key_tuples( - attrname, obj, parent, anon_map, bindparams - ) - - def visit_fromclause_ordered_set( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - return ( - attrname, - tuple([elem._gen_cache_key(anon_map, bindparams) for elem in obj]), - ) - - def visit_clauseelement_unordered_set( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - cache_keys = [ - elem._gen_cache_key(anon_map, bindparams) for elem in obj - ] - return ( - attrname, - tuple( - sorted(cache_keys) - ), # cache keys all start with (id_, class) - ) - - def visit_named_ddl_element( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return (attrname, obj.name) - - def visit_prefix_sequence( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - - return ( - attrname, - tuple( - [ - (clause._gen_cache_key(anon_map, bindparams), strval) - for clause, strval in obj - ] - ), - ) - - def visit_setup_join_tuple( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return tuple( - ( - target._gen_cache_key(anon_map, bindparams), - ( - onclause._gen_cache_key(anon_map, bindparams) - if onclause is not None - else None - ), - ( - from_._gen_cache_key(anon_map, bindparams) - if from_ is not None - else None - ), - tuple([(key, flags[key]) for key in sorted(flags)]), - ) - for (target, onclause, from_, flags) in obj - ) - - def visit_table_hint_list( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - if not obj: - return () - - return ( - attrname, - tuple( - [ - ( - clause._gen_cache_key(anon_map, bindparams), - dialect_name, - text, - ) - for (clause, dialect_name), text in obj.items() - ] - ), - ) - - def visit_plain_dict( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return (attrname, tuple([(key, obj[key]) for key in sorted(obj)])) - - def visit_dialect_options( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - tuple( - ( - dialect_name, - tuple( - [ - (key, obj[dialect_name][key]) - for key in sorted(obj[dialect_name]) - ] - ), - ) - for dialect_name in sorted(obj) - ), - ) - - def visit_string_clauseelement_dict( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - tuple( - (key, obj[key]._gen_cache_key(anon_map, bindparams)) - for key in sorted(obj) - ), - ) - - def visit_string_multi_dict( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - tuple( - ( - key, - ( - value._gen_cache_key(anon_map, bindparams) - if isinstance(value, HasCacheKey) - else value - ), - ) - for key, value in [(key, obj[key]) for key in sorted(obj)] - ), - ) - - def visit_fromclause_canonical_column_collection( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - # inlining into the internals of ColumnCollection - return ( - attrname, - tuple( - col._gen_cache_key(anon_map, bindparams) - for k, col, _ in obj._collection - ), - ) - - def visit_unknown_structure( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - anon_map[NO_CACHE] = True - return () - - def visit_dml_ordered_values( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - return ( - attrname, - tuple( - ( - ( - key._gen_cache_key(anon_map, bindparams) - if hasattr(key, "__clause_element__") - else key - ), - value._gen_cache_key(anon_map, bindparams), - ) - for key, value in obj - ), - ) - - def visit_dml_values( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - # in py37 we can assume two dictionaries created in the same - # insert ordering will retain that sorting - return ( - attrname, - tuple( - ( - ( - k._gen_cache_key(anon_map, bindparams) - if hasattr(k, "__clause_element__") - else k - ), - obj[k]._gen_cache_key(anon_map, bindparams), - ) - for k in obj - ), - ) - - def visit_dml_multi_values( - self, - attrname: str, - obj: Any, - parent: Any, - anon_map: anon_map, - bindparams: List[BindParameter[Any]], - ) -> Tuple[Any, ...]: - # multivalues are simply not cacheable right now - anon_map[NO_CACHE] = True - return () - - -_cache_key_traversal_visitor = _CacheKeyTraversal() diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py deleted file mode 100644 index 22d6091..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/coercions.py +++ /dev/null @@ -1,1389 +0,0 @@ -# sql/coercions.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -from __future__ import annotations - -import collections.abc as collections_abc -import numbers -import re -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Iterable -from typing import Iterator -from typing import List -from typing import NoReturn -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 typing import Union - -from . import operators -from . import roles -from . import visitors -from ._typing import is_from_clause -from .base import ExecutableOption -from .base import Options -from .cache_key import HasCacheKey -from .visitors import Visitable -from .. import exc -from .. import inspection -from .. import util -from ..util.typing import Literal - -if typing.TYPE_CHECKING: - # elements lambdas schema selectable are set by __init__ - from . import elements - from . import lambdas - from . import schema - from . import selectable - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnsClauseArgument - from ._typing import _DDLColumnArgument - from ._typing import _DMLTableArgument - from ._typing import _FromClauseArgument - from .dml import _DMLTableElement - from .elements import BindParameter - from .elements import ClauseElement - from .elements import ColumnClause - from .elements import ColumnElement - from .elements import DQLDMLClauseElement - from .elements import NamedColumn - from .elements import SQLCoreOperations - from .schema import Column - from .selectable import _ColumnsClauseElement - from .selectable import _JoinTargetProtocol - from .selectable import FromClause - from .selectable import HasCTE - from .selectable import SelectBase - from .selectable import Subquery - from .visitors import _TraverseCallableType - -_SR = TypeVar("_SR", bound=roles.SQLRole) -_F = TypeVar("_F", bound=Callable[..., Any]) -_StringOnlyR = TypeVar("_StringOnlyR", bound=roles.StringRole) -_T = TypeVar("_T", bound=Any) - - -def _is_literal(element): - """Return whether or not the element is a "literal" in the context - of a SQL expression construct. - - """ - - return not isinstance( - element, - (Visitable, schema.SchemaEventTarget), - ) and not hasattr(element, "__clause_element__") - - -def _deep_is_literal(element): - """Return whether or not the element is a "literal" in the context - of a SQL expression construct. - - does a deeper more esoteric check than _is_literal. is used - for lambda elements that have to distinguish values that would - be bound vs. not without any context. - - """ - - if isinstance(element, collections_abc.Sequence) and not isinstance( - element, str - ): - for elem in element: - if not _deep_is_literal(elem): - return False - else: - return True - - return ( - not isinstance( - element, - ( - Visitable, - schema.SchemaEventTarget, - HasCacheKey, - Options, - util.langhelpers.symbol, - ), - ) - and not hasattr(element, "__clause_element__") - and ( - not isinstance(element, type) - or not issubclass(element, HasCacheKey) - ) - ) - - -def _document_text_coercion( - paramname: str, meth_rst: str, param_rst: str -) -> Callable[[_F], _F]: - return util.add_parameter_text( - paramname, - ( - ".. warning:: " - "The %s argument to %s can be passed as a Python string argument, " - "which will be treated " - "as **trusted SQL text** and rendered as given. **DO NOT PASS " - "UNTRUSTED INPUT TO THIS PARAMETER**." - ) - % (param_rst, meth_rst), - ) - - -def _expression_collection_was_a_list( - attrname: str, - fnname: str, - args: Union[Sequence[_T], Sequence[Sequence[_T]]], -) -> Sequence[_T]: - if args and isinstance(args[0], (list, set, dict)) and len(args) == 1: - if isinstance(args[0], list): - raise exc.ArgumentError( - f'The "{attrname}" argument to {fnname}(), when ' - "referring to a sequence " - "of items, is now passed as a series of positional " - "elements, rather than as a list. " - ) - return cast("Sequence[_T]", args[0]) - - return cast("Sequence[_T]", args) - - -@overload -def expect( - role: Type[roles.TruncatedLabelRole], - element: Any, - **kw: Any, -) -> str: ... - - -@overload -def expect( - role: Type[roles.DMLColumnRole], - element: Any, - *, - as_key: Literal[True] = ..., - **kw: Any, -) -> str: ... - - -@overload -def expect( - role: Type[roles.LiteralValueRole], - element: Any, - **kw: Any, -) -> BindParameter[Any]: ... - - -@overload -def expect( - role: Type[roles.DDLReferredColumnRole], - element: Any, - **kw: Any, -) -> Column[Any]: ... - - -@overload -def expect( - role: Type[roles.DDLConstraintColumnRole], - element: Any, - **kw: Any, -) -> Union[Column[Any], str]: ... - - -@overload -def expect( - role: Type[roles.StatementOptionRole], - element: Any, - **kw: Any, -) -> DQLDMLClauseElement: ... - - -@overload -def expect( - role: Type[roles.LabeledColumnExprRole[Any]], - element: _ColumnExpressionArgument[_T], - **kw: Any, -) -> NamedColumn[_T]: ... - - -@overload -def expect( - role: Union[ - Type[roles.ExpressionElementRole[Any]], - Type[roles.LimitOffsetRole], - Type[roles.WhereHavingRole], - ], - element: _ColumnExpressionArgument[_T], - **kw: Any, -) -> ColumnElement[_T]: ... - - -@overload -def expect( - role: Union[ - Type[roles.ExpressionElementRole[Any]], - Type[roles.LimitOffsetRole], - Type[roles.WhereHavingRole], - Type[roles.OnClauseRole], - Type[roles.ColumnArgumentRole], - ], - element: Any, - **kw: Any, -) -> ColumnElement[Any]: ... - - -@overload -def expect( - role: Type[roles.DMLTableRole], - element: _DMLTableArgument, - **kw: Any, -) -> _DMLTableElement: ... - - -@overload -def expect( - role: Type[roles.HasCTERole], - element: HasCTE, - **kw: Any, -) -> HasCTE: ... - - -@overload -def expect( - role: Type[roles.SelectStatementRole], - element: SelectBase, - **kw: Any, -) -> SelectBase: ... - - -@overload -def expect( - role: Type[roles.FromClauseRole], - element: _FromClauseArgument, - **kw: Any, -) -> FromClause: ... - - -@overload -def expect( - role: Type[roles.FromClauseRole], - element: SelectBase, - *, - explicit_subquery: Literal[True] = ..., - **kw: Any, -) -> Subquery: ... - - -@overload -def expect( - role: Type[roles.ColumnsClauseRole], - element: _ColumnsClauseArgument[Any], - **kw: Any, -) -> _ColumnsClauseElement: ... - - -@overload -def expect( - role: Type[roles.JoinTargetRole], - element: _JoinTargetProtocol, - **kw: Any, -) -> _JoinTargetProtocol: ... - - -# catchall for not-yet-implemented overloads -@overload -def expect( - role: Type[_SR], - element: Any, - **kw: Any, -) -> Any: ... - - -def expect( - role: Type[_SR], - element: Any, - *, - apply_propagate_attrs: Optional[ClauseElement] = None, - argname: Optional[str] = None, - post_inspect: bool = False, - disable_inspection: bool = False, - **kw: Any, -) -> Any: - if ( - role.allows_lambda - # note callable() will not invoke a __getattr__() method, whereas - # hasattr(obj, "__call__") will. by keeping the callable() check here - # we prevent most needless calls to hasattr() and therefore - # __getattr__(), which is present on ColumnElement. - and callable(element) - and hasattr(element, "__code__") - ): - return lambdas.LambdaElement( - element, - role, - lambdas.LambdaOptions(**kw), - apply_propagate_attrs=apply_propagate_attrs, - ) - - # major case is that we are given a ClauseElement already, skip more - # elaborate logic up front if possible - impl = _impl_lookup[role] - - original_element = element - - if not isinstance( - element, - ( - elements.CompilerElement, - schema.SchemaItem, - schema.FetchedValue, - lambdas.PyWrapper, - ), - ): - resolved = None - - if impl._resolve_literal_only: - resolved = impl._literal_coercion(element, **kw) - else: - original_element = element - - is_clause_element = False - - # this is a special performance optimization for ORM - # joins used by JoinTargetImpl that we don't go through the - # work of creating __clause_element__() when we only need the - # original QueryableAttribute, as the former will do clause - # adaption and all that which is just thrown away here. - if ( - impl._skip_clauseelement_for_target_match - and isinstance(element, role) - and hasattr(element, "__clause_element__") - ): - is_clause_element = True - else: - while hasattr(element, "__clause_element__"): - is_clause_element = True - - if not getattr(element, "is_clause_element", False): - element = element.__clause_element__() - else: - break - - if not is_clause_element: - if impl._use_inspection and not disable_inspection: - insp = inspection.inspect(element, raiseerr=False) - if insp is not None: - if post_inspect: - insp._post_inspect - try: - resolved = insp.__clause_element__() - except AttributeError: - impl._raise_for_expected(original_element, argname) - - if resolved is None: - resolved = impl._literal_coercion( - element, argname=argname, **kw - ) - else: - resolved = element - elif isinstance(element, lambdas.PyWrapper): - resolved = element._sa__py_wrapper_literal(**kw) - else: - resolved = element - - if apply_propagate_attrs is not None: - if typing.TYPE_CHECKING: - assert isinstance(resolved, (SQLCoreOperations, ClauseElement)) - - if not apply_propagate_attrs._propagate_attrs and getattr( - resolved, "_propagate_attrs", None - ): - apply_propagate_attrs._propagate_attrs = resolved._propagate_attrs - - if impl._role_class in resolved.__class__.__mro__: - if impl._post_coercion: - resolved = impl._post_coercion( - resolved, - argname=argname, - original_element=original_element, - **kw, - ) - return resolved - else: - return impl._implicit_coercions( - original_element, resolved, argname=argname, **kw - ) - - -def expect_as_key( - role: Type[roles.DMLColumnRole], element: Any, **kw: Any -) -> str: - kw.pop("as_key", None) - return expect(role, element, as_key=True, **kw) - - -def expect_col_expression_collection( - role: Type[roles.DDLConstraintColumnRole], - expressions: Iterable[_DDLColumnArgument], -) -> Iterator[ - Tuple[ - Union[str, Column[Any]], - Optional[ColumnClause[Any]], - Optional[str], - Optional[Union[Column[Any], str]], - ] -]: - for expr in expressions: - strname = None - column = None - - resolved: Union[Column[Any], str] = expect(role, expr) - if isinstance(resolved, str): - assert isinstance(expr, str) - strname = resolved = expr - else: - cols: List[Column[Any]] = [] - col_append: _TraverseCallableType[Column[Any]] = cols.append - visitors.traverse(resolved, {}, {"column": col_append}) - if cols: - column = cols[0] - add_element = column if column is not None else strname - - yield resolved, column, strname, add_element - - -class RoleImpl: - __slots__ = ("_role_class", "name", "_use_inspection") - - def _literal_coercion(self, element, **kw): - raise NotImplementedError() - - _post_coercion: Any = None - _resolve_literal_only = False - _skip_clauseelement_for_target_match = False - - def __init__(self, role_class): - self._role_class = role_class - self.name = role_class._role_name - self._use_inspection = issubclass(role_class, roles.UsesInspection) - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - self._raise_for_expected(element, argname, resolved) - - def _raise_for_expected( - self, - element: Any, - argname: Optional[str] = None, - resolved: Optional[Any] = None, - advice: Optional[str] = None, - code: Optional[str] = None, - err: Optional[Exception] = None, - **kw: Any, - ) -> NoReturn: - if resolved is not None and resolved is not element: - got = "%r object resolved from %r object" % (resolved, element) - else: - got = repr(element) - - if argname: - msg = "%s expected for argument %r; got %s." % ( - self.name, - argname, - got, - ) - else: - msg = "%s expected, got %s." % (self.name, got) - - if advice: - msg += " " + advice - - raise exc.ArgumentError(msg, code=code) from err - - -class _Deannotate: - __slots__ = () - - def _post_coercion(self, resolved, **kw): - from .util import _deep_deannotate - - return _deep_deannotate(resolved) - - -class _StringOnly: - __slots__ = () - - _resolve_literal_only = True - - -class _ReturnsStringKey(RoleImpl): - __slots__ = () - - def _implicit_coercions(self, element, resolved, argname=None, **kw): - if isinstance(element, str): - return element - else: - self._raise_for_expected(element, argname, resolved) - - def _literal_coercion(self, element, **kw): - return element - - -class _ColumnCoercions(RoleImpl): - __slots__ = () - - def _warn_for_scalar_subquery_coercion(self): - util.warn( - "implicitly coercing SELECT object to scalar subquery; " - "please use the .scalar_subquery() method to produce a scalar " - "subquery.", - ) - - def _implicit_coercions(self, element, resolved, argname=None, **kw): - original_element = element - if not getattr(resolved, "is_clause_element", False): - self._raise_for_expected(original_element, argname, resolved) - elif resolved._is_select_base: - self._warn_for_scalar_subquery_coercion() - return resolved.scalar_subquery() - elif resolved._is_from_clause and isinstance( - resolved, selectable.Subquery - ): - self._warn_for_scalar_subquery_coercion() - return resolved.element.scalar_subquery() - elif self._role_class.allows_lambda and resolved._is_lambda_element: - return resolved - else: - self._raise_for_expected(original_element, argname, resolved) - - -def _no_text_coercion( - element: Any, - argname: Optional[str] = None, - exc_cls: Type[exc.SQLAlchemyError] = exc.ArgumentError, - extra: Optional[str] = None, - err: Optional[Exception] = None, -) -> NoReturn: - raise exc_cls( - "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be " - "explicitly declared as text(%(expr)r)" - % { - "expr": util.ellipses_string(element), - "argname": "for argument %s" % (argname,) if argname else "", - "extra": "%s " % extra if extra else "", - } - ) from err - - -class _NoTextCoercion(RoleImpl): - __slots__ = () - - def _literal_coercion(self, element, argname=None, **kw): - if isinstance(element, str) and issubclass( - elements.TextClause, self._role_class - ): - _no_text_coercion(element, argname) - else: - self._raise_for_expected(element, argname) - - -class _CoerceLiterals(RoleImpl): - __slots__ = () - _coerce_consts = False - _coerce_star = False - _coerce_numerics = False - - def _text_coercion(self, element, argname=None): - return _no_text_coercion(element, argname) - - def _literal_coercion(self, element, argname=None, **kw): - if isinstance(element, str): - if self._coerce_star and element == "*": - return elements.ColumnClause("*", is_literal=True) - else: - return self._text_coercion(element, argname, **kw) - - if self._coerce_consts: - if element is None: - return elements.Null() - elif element is False: - return elements.False_() - elif element is True: - return elements.True_() - - if self._coerce_numerics and isinstance(element, (numbers.Number)): - return elements.ColumnClause(str(element), is_literal=True) - - self._raise_for_expected(element, argname) - - -class LiteralValueImpl(RoleImpl): - _resolve_literal_only = True - - def _implicit_coercions( - self, - element, - resolved, - argname, - type_=None, - literal_execute=False, - **kw, - ): - if not _is_literal(resolved): - self._raise_for_expected( - element, resolved=resolved, argname=argname, **kw - ) - - return elements.BindParameter( - None, - element, - type_=type_, - unique=True, - literal_execute=literal_execute, - ) - - def _literal_coercion(self, element, argname=None, type_=None, **kw): - return element - - -class _SelectIsNotFrom(RoleImpl): - __slots__ = () - - def _raise_for_expected( - self, - element: Any, - argname: Optional[str] = None, - resolved: Optional[Any] = None, - advice: Optional[str] = None, - code: Optional[str] = None, - err: Optional[Exception] = None, - **kw: Any, - ) -> NoReturn: - if ( - not advice - and isinstance(element, roles.SelectStatementRole) - or isinstance(resolved, roles.SelectStatementRole) - ): - advice = ( - "To create a " - "FROM clause from a %s object, use the .subquery() method." - % (resolved.__class__ if resolved is not None else element,) - ) - code = "89ve" - else: - code = None - - super()._raise_for_expected( - element, - argname=argname, - resolved=resolved, - advice=advice, - code=code, - err=err, - **kw, - ) - # never reached - assert False - - -class HasCacheKeyImpl(RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if isinstance(element, HasCacheKey): - return element - else: - self._raise_for_expected(element, argname, resolved) - - def _literal_coercion(self, element, **kw): - return element - - -class ExecutableOptionImpl(RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if isinstance(element, ExecutableOption): - return element - else: - self._raise_for_expected(element, argname, resolved) - - def _literal_coercion(self, element, **kw): - return element - - -class ExpressionElementImpl(_ColumnCoercions, RoleImpl): - __slots__ = () - - def _literal_coercion( - self, element, name=None, type_=None, argname=None, is_crud=False, **kw - ): - if ( - element is None - and not is_crud - and (type_ is None or not type_.should_evaluate_none) - ): - # TODO: there's no test coverage now for the - # "should_evaluate_none" part of this, as outside of "crud" this - # codepath is not normally used except in some special cases - return elements.Null() - else: - try: - return elements.BindParameter( - name, element, type_, unique=True, _is_crud=is_crud - ) - except exc.ArgumentError as err: - self._raise_for_expected(element, err=err) - - def _raise_for_expected(self, element, argname=None, resolved=None, **kw): - # select uses implicit coercion with warning instead of raising - if isinstance(element, selectable.Values): - advice = ( - "To create a column expression from a VALUES clause, " - "use the .scalar_values() method." - ) - elif isinstance(element, roles.AnonymizedFromClauseRole): - advice = ( - "To create a column expression from a FROM clause row " - "as a whole, use the .table_valued() method." - ) - else: - advice = None - - return super()._raise_for_expected( - element, argname=argname, resolved=resolved, advice=advice, **kw - ) - - -class BinaryElementImpl(ExpressionElementImpl, RoleImpl): - __slots__ = () - - def _literal_coercion( - self, element, expr, operator, bindparam_type=None, argname=None, **kw - ): - try: - return expr._bind_param(operator, element, type_=bindparam_type) - except exc.ArgumentError as err: - self._raise_for_expected(element, err=err) - - def _post_coercion(self, resolved, expr, bindparam_type=None, **kw): - if resolved.type._isnull and not expr.type._isnull: - resolved = resolved._with_binary_element_type( - bindparam_type if bindparam_type is not None else expr.type - ) - return resolved - - -class InElementImpl(RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if resolved._is_from_clause: - if ( - isinstance(resolved, selectable.Alias) - and resolved.element._is_select_base - ): - self._warn_for_implicit_coercion(resolved) - return self._post_coercion(resolved.element, **kw) - else: - self._warn_for_implicit_coercion(resolved) - return self._post_coercion(resolved.select(), **kw) - else: - self._raise_for_expected(element, argname, resolved) - - def _warn_for_implicit_coercion(self, elem): - util.warn( - "Coercing %s object into a select() for use in IN(); " - "please pass a select() construct explicitly" - % (elem.__class__.__name__) - ) - - def _literal_coercion(self, element, expr, operator, **kw): - if util.is_non_string_iterable(element): - non_literal_expressions: Dict[ - Optional[operators.ColumnOperators], - operators.ColumnOperators, - ] = {} - element = list(element) - for o in element: - if not _is_literal(o): - if not isinstance(o, operators.ColumnOperators): - self._raise_for_expected(element, **kw) - - else: - non_literal_expressions[o] = o - elif o is None: - non_literal_expressions[o] = elements.Null() - - if non_literal_expressions: - return elements.ClauseList( - *[ - ( - non_literal_expressions[o] - if o in non_literal_expressions - else expr._bind_param(operator, o) - ) - for o in element - ] - ) - else: - return expr._bind_param(operator, element, expanding=True) - - else: - self._raise_for_expected(element, **kw) - - def _post_coercion(self, element, expr, operator, **kw): - if element._is_select_base: - # for IN, we are doing scalar_subquery() coercion without - # a warning - return element.scalar_subquery() - elif isinstance(element, elements.ClauseList): - assert not len(element.clauses) == 0 - return element.self_group(against=operator) - - elif isinstance(element, elements.BindParameter): - element = element._clone(maintain_key=True) - element.expanding = True - element.expand_op = operator - - return element - elif isinstance(element, selectable.Values): - return element.scalar_values() - else: - return element - - -class OnClauseImpl(_ColumnCoercions, RoleImpl): - __slots__ = () - - _coerce_consts = True - - def _literal_coercion( - self, element, name=None, type_=None, argname=None, is_crud=False, **kw - ): - self._raise_for_expected(element) - - def _post_coercion(self, resolved, original_element=None, **kw): - # this is a hack right now as we want to use coercion on an - # ORM InstrumentedAttribute, but we want to return the object - # itself if it is one, not its clause element. - # ORM context _join and _legacy_join() would need to be improved - # to look for annotations in a clause element form. - if isinstance(original_element, roles.JoinTargetRole): - return original_element - return resolved - - -class WhereHavingImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl): - __slots__ = () - - _coerce_consts = True - - def _text_coercion(self, element, argname=None): - return _no_text_coercion(element, argname) - - -class StatementOptionImpl(_CoerceLiterals, RoleImpl): - __slots__ = () - - _coerce_consts = True - - def _text_coercion(self, element, argname=None): - return elements.TextClause(element) - - -class ColumnArgumentImpl(_NoTextCoercion, RoleImpl): - __slots__ = () - - -class ColumnArgumentOrKeyImpl(_ReturnsStringKey, RoleImpl): - __slots__ = () - - -class StrAsPlainColumnImpl(_CoerceLiterals, RoleImpl): - __slots__ = () - - def _text_coercion(self, element, argname=None): - return elements.ColumnClause(element) - - -class ByOfImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl, roles.ByOfRole): - __slots__ = () - - _coerce_consts = True - - def _text_coercion(self, element, argname=None): - return elements._textual_label_reference(element) - - -class OrderByImpl(ByOfImpl, RoleImpl): - __slots__ = () - - def _post_coercion(self, resolved, **kw): - if ( - isinstance(resolved, self._role_class) - and resolved._order_by_label_element is not None - ): - return elements._label_reference(resolved) - else: - return resolved - - -class GroupByImpl(ByOfImpl, RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if is_from_clause(resolved): - return elements.ClauseList(*resolved.c) - else: - return resolved - - -class DMLColumnImpl(_ReturnsStringKey, RoleImpl): - __slots__ = () - - def _post_coercion(self, element, as_key=False, **kw): - if as_key: - return element.key - else: - return element - - -class ConstExprImpl(RoleImpl): - __slots__ = () - - def _literal_coercion(self, element, argname=None, **kw): - if element is None: - return elements.Null() - elif element is False: - return elements.False_() - elif element is True: - return elements.True_() - else: - self._raise_for_expected(element, argname) - - -class TruncatedLabelImpl(_StringOnly, RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if isinstance(element, str): - return resolved - else: - self._raise_for_expected(element, argname, resolved) - - def _literal_coercion(self, element, argname=None, **kw): - """coerce the given value to :class:`._truncated_label`. - - Existing :class:`._truncated_label` and - :class:`._anonymous_label` objects are passed - unchanged. - """ - - if isinstance(element, elements._truncated_label): - return element - else: - return elements._truncated_label(element) - - -class DDLExpressionImpl(_Deannotate, _CoerceLiterals, RoleImpl): - __slots__ = () - - _coerce_consts = True - - def _text_coercion(self, element, argname=None): - # see #5754 for why we can't easily deprecate this coercion. - # essentially expressions like postgresql_where would have to be - # text() as they come back from reflection and we don't want to - # have text() elements wired into the inspection dictionaries. - return elements.TextClause(element) - - -class DDLConstraintColumnImpl(_Deannotate, _ReturnsStringKey, RoleImpl): - __slots__ = () - - -class DDLReferredColumnImpl(DDLConstraintColumnImpl): - __slots__ = () - - -class LimitOffsetImpl(RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if resolved is None: - return None - else: - self._raise_for_expected(element, argname, resolved) - - def _literal_coercion(self, element, name, type_, **kw): - if element is None: - return None - else: - value = util.asint(element) - return selectable._OffsetLimitParam( - name, value, type_=type_, unique=True - ) - - -class LabeledColumnExprImpl(ExpressionElementImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if isinstance(resolved, roles.ExpressionElementRole): - return resolved.label(None) - else: - new = super()._implicit_coercions( - element, resolved, argname=argname, **kw - ) - if isinstance(new, roles.ExpressionElementRole): - return new.label(None) - else: - self._raise_for_expected(element, argname, resolved) - - -class ColumnsClauseImpl(_SelectIsNotFrom, _CoerceLiterals, RoleImpl): - __slots__ = () - - _coerce_consts = True - _coerce_numerics = True - _coerce_star = True - - _guess_straight_column = re.compile(r"^\w\S*$", re.I) - - def _raise_for_expected( - self, element, argname=None, resolved=None, advice=None, **kw - ): - if not advice and isinstance(element, list): - advice = ( - f"Did you mean to say select(" - f"{', '.join(repr(e) for e in element)})?" - ) - - return super()._raise_for_expected( - element, argname=argname, resolved=resolved, advice=advice, **kw - ) - - def _text_coercion(self, element, argname=None): - element = str(element) - - guess_is_literal = not self._guess_straight_column.match(element) - raise exc.ArgumentError( - "Textual column expression %(column)r %(argname)sshould be " - "explicitly declared with text(%(column)r), " - "or use %(literal_column)s(%(column)r) " - "for more specificity" - % { - "column": util.ellipses_string(element), - "argname": "for argument %s" % (argname,) if argname else "", - "literal_column": ( - "literal_column" if guess_is_literal else "column" - ), - } - ) - - -class ReturnsRowsImpl(RoleImpl): - __slots__ = () - - -class StatementImpl(_CoerceLiterals, RoleImpl): - __slots__ = () - - def _post_coercion(self, resolved, original_element, argname=None, **kw): - if resolved is not original_element and not isinstance( - original_element, str - ): - # use same method as Connection uses; this will later raise - # ObjectNotExecutableError - try: - original_element._execute_on_connection - except AttributeError: - util.warn_deprecated( - "Object %r should not be used directly in a SQL statement " - "context, such as passing to methods such as " - "session.execute(). This usage will be disallowed in a " - "future release. " - "Please use Core select() / update() / delete() etc. " - "with Session.execute() and other statement execution " - "methods." % original_element, - "1.4", - ) - - return resolved - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if resolved._is_lambda_element: - return resolved - else: - return super()._implicit_coercions( - element, resolved, argname=argname, **kw - ) - - -class SelectStatementImpl(_NoTextCoercion, RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if resolved._is_text_clause: - return resolved.columns() - else: - self._raise_for_expected(element, argname, resolved) - - -class HasCTEImpl(ReturnsRowsImpl): - __slots__ = () - - -class IsCTEImpl(RoleImpl): - __slots__ = () - - -class JoinTargetImpl(RoleImpl): - __slots__ = () - - _skip_clauseelement_for_target_match = True - - def _literal_coercion(self, element, argname=None, **kw): - self._raise_for_expected(element, argname) - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - legacy: bool = False, - **kw: Any, - ) -> Any: - if isinstance(element, roles.JoinTargetRole): - # note that this codepath no longer occurs as of - # #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match - # were set to False. - return element - elif legacy and resolved._is_select_base: - util.warn_deprecated( - "Implicit coercion of SELECT and textual SELECT " - "constructs into FROM clauses is deprecated; please call " - ".subquery() on any Core select or ORM Query object in " - "order to produce a subquery object.", - version="1.4", - ) - # TODO: doing _implicit_subquery here causes tests to fail, - # how was this working before? probably that ORM - # join logic treated it as a select and subquery would happen - # in _ORMJoin->Join - return resolved - else: - self._raise_for_expected(element, argname, resolved) - - -class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - explicit_subquery: bool = False, - allow_select: bool = True, - **kw: Any, - ) -> Any: - if resolved._is_select_base: - if explicit_subquery: - return resolved.subquery() - elif allow_select: - util.warn_deprecated( - "Implicit coercion of SELECT and textual SELECT " - "constructs into FROM clauses is deprecated; please call " - ".subquery() on any Core select or ORM Query object in " - "order to produce a subquery object.", - version="1.4", - ) - return resolved._implicit_subquery - elif resolved._is_text_clause: - return resolved - else: - self._raise_for_expected(element, argname, resolved) - - def _post_coercion(self, element, deannotate=False, **kw): - if deannotate: - return element._deannotate() - else: - return element - - -class StrictFromClauseImpl(FromClauseImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - explicit_subquery: bool = False, - allow_select: bool = False, - **kw: Any, - ) -> Any: - if resolved._is_select_base and allow_select: - util.warn_deprecated( - "Implicit coercion of SELECT and textual SELECT constructs " - "into FROM clauses is deprecated; please call .subquery() " - "on any Core select or ORM Query object in order to produce a " - "subquery object.", - version="1.4", - ) - return resolved._implicit_subquery - else: - self._raise_for_expected(element, argname, resolved) - - -class AnonymizedFromClauseImpl(StrictFromClauseImpl): - __slots__ = () - - def _post_coercion(self, element, flat=False, name=None, **kw): - assert name is None - - return element._anonymous_fromclause(flat=flat) - - -class DMLTableImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl): - __slots__ = () - - def _post_coercion(self, element, **kw): - if "dml_table" in element._annotations: - return element._annotations["dml_table"] - else: - return element - - -class DMLSelectImpl(_NoTextCoercion, RoleImpl): - __slots__ = () - - def _implicit_coercions( - self, - element: Any, - resolved: Any, - argname: Optional[str] = None, - **kw: Any, - ) -> Any: - if resolved._is_from_clause: - if ( - isinstance(resolved, selectable.Alias) - and resolved.element._is_select_base - ): - return resolved.element - else: - return resolved.select() - else: - self._raise_for_expected(element, argname, resolved) - - -class CompoundElementImpl(_NoTextCoercion, RoleImpl): - __slots__ = () - - def _raise_for_expected(self, element, argname=None, resolved=None, **kw): - if isinstance(element, roles.FromClauseRole): - if element._is_subquery: - advice = ( - "Use the plain select() object without " - "calling .subquery() or .alias()." - ) - else: - advice = ( - "To SELECT from any FROM clause, use the .select() method." - ) - else: - advice = None - return super()._raise_for_expected( - element, argname=argname, resolved=resolved, advice=advice, **kw - ) - - -_impl_lookup = {} - - -for name in dir(roles): - cls = getattr(roles, name) - if name.endswith("Role"): - name = name.replace("Role", "Impl") - if name in globals(): - impl = globals()[name](cls) - _impl_lookup[cls] = impl - -if not TYPE_CHECKING: - ee_impl = _impl_lookup[roles.ExpressionElementRole] - - for py_type in (int, bool, str, float): - _impl_lookup[roles.ExpressionElementRole[py_type]] = ee_impl diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/compiler.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/compiler.py deleted file mode 100644 index c354ba8..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/compiler.py +++ /dev/null @@ -1,7811 +0,0 @@ -# sql/compiler.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""Base SQL and DDL compiler implementations. - -Classes provided include: - -:class:`.compiler.SQLCompiler` - renders SQL -strings - -:class:`.compiler.DDLCompiler` - renders DDL -(data definition language) strings - -:class:`.compiler.GenericTypeCompiler` - renders -type specification strings. - -To generate user-defined SQL strings, see -:doc:`/ext/compiler`. - -""" -from __future__ import annotations - -import collections -import collections.abc as collections_abc -import contextlib -from enum import IntEnum -import functools -import itertools -import operator -import re -from time import perf_counter -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import ClassVar -from typing import Dict -from typing import FrozenSet -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping -from typing import MutableMapping -from typing import NamedTuple -from typing import NoReturn -from typing import Optional -from typing import Pattern -from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import Union - -from . import base -from . import coercions -from . import crud -from . import elements -from . import functions -from . import operators -from . import roles -from . import schema -from . import selectable -from . import sqltypes -from . import util as sql_util -from ._typing import is_column_element -from ._typing import is_dml -from .base import _de_clone -from .base import _from_objects -from .base import _NONE_NAME -from .base import _SentinelDefaultCharacterization -from .base import Executable -from .base import NO_ARG -from .elements import ClauseElement -from .elements import quoted_name -from .schema import Column -from .sqltypes import TupleType -from .type_api import TypeEngine -from .visitors import prefix_anon_map -from .visitors import Visitable -from .. import exc -from .. import util -from ..util import FastIntFlag -from ..util.typing import Literal -from ..util.typing import Protocol -from ..util.typing import TypedDict - -if typing.TYPE_CHECKING: - from .annotation import _AnnotationDict - from .base import _AmbiguousTableNameMap - from .base import CompileState - from .cache_key import CacheKey - from .ddl import ExecutableDDLElement - from .dml import Insert - from .dml import UpdateBase - from .dml import ValuesBase - from .elements import _truncated_label - from .elements import BindParameter - from .elements import ColumnClause - from .elements import ColumnElement - from .elements import Label - from .functions import Function - from .schema import Table - from .selectable import AliasedReturnsRows - from .selectable import CompoundSelectState - from .selectable import CTE - from .selectable import FromClause - from .selectable import NamedFromClause - from .selectable import ReturnsRows - from .selectable import Select - from .selectable import SelectState - from .type_api import _BindProcessorType - from ..engine.cursor import CursorResultMetaData - from ..engine.interfaces import _CoreSingleExecuteParams - from ..engine.interfaces import _DBAPIAnyExecuteParams - from ..engine.interfaces import _DBAPIMultiExecuteParams - from ..engine.interfaces import _DBAPISingleExecuteParams - from ..engine.interfaces import _ExecuteOptions - from ..engine.interfaces import _GenericSetInputSizesType - from ..engine.interfaces import _MutableCoreSingleExecuteParams - from ..engine.interfaces import Dialect - from ..engine.interfaces import SchemaTranslateMapType - -_FromHintsType = Dict["FromClause", str] - -RESERVED_WORDS = { - "all", - "analyse", - "analyze", - "and", - "any", - "array", - "as", - "asc", - "asymmetric", - "authorization", - "between", - "binary", - "both", - "case", - "cast", - "check", - "collate", - "column", - "constraint", - "create", - "cross", - "current_date", - "current_role", - "current_time", - "current_timestamp", - "current_user", - "default", - "deferrable", - "desc", - "distinct", - "do", - "else", - "end", - "except", - "false", - "for", - "foreign", - "freeze", - "from", - "full", - "grant", - "group", - "having", - "ilike", - "in", - "initially", - "inner", - "intersect", - "into", - "is", - "isnull", - "join", - "leading", - "left", - "like", - "limit", - "localtime", - "localtimestamp", - "natural", - "new", - "not", - "notnull", - "null", - "off", - "offset", - "old", - "on", - "only", - "or", - "order", - "outer", - "overlaps", - "placing", - "primary", - "references", - "right", - "select", - "session_user", - "set", - "similar", - "some", - "symmetric", - "table", - "then", - "to", - "trailing", - "true", - "union", - "unique", - "user", - "using", - "verbose", - "when", - "where", -} - -LEGAL_CHARACTERS = re.compile(r"^[A-Z0-9_$]+$", re.I) -LEGAL_CHARACTERS_PLUS_SPACE = re.compile(r"^[A-Z0-9_ $]+$", re.I) -ILLEGAL_INITIAL_CHARACTERS = {str(x) for x in range(0, 10)}.union(["$"]) - -FK_ON_DELETE = re.compile( - r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I -) -FK_ON_UPDATE = re.compile( - r"^(?:RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT)$", re.I -) -FK_INITIALLY = re.compile(r"^(?:DEFERRED|IMMEDIATE)$", re.I) -BIND_PARAMS = re.compile(r"(?<![:\w\$\x5c]):([\w\$]+)(?![:\w\$])", re.UNICODE) -BIND_PARAMS_ESC = re.compile(r"\x5c(:[\w\$]*)(?![:\w\$])", re.UNICODE) - -_pyformat_template = "%%(%(name)s)s" -BIND_TEMPLATES = { - "pyformat": _pyformat_template, - "qmark": "?", - "format": "%%s", - "numeric": ":[_POSITION]", - "numeric_dollar": "$[_POSITION]", - "named": ":%(name)s", -} - - -OPERATORS = { - # binary - operators.and_: " AND ", - operators.or_: " OR ", - operators.add: " + ", - operators.mul: " * ", - operators.sub: " - ", - operators.mod: " % ", - operators.neg: "-", - operators.lt: " < ", - operators.le: " <= ", - operators.ne: " != ", - operators.gt: " > ", - operators.ge: " >= ", - operators.eq: " = ", - operators.is_distinct_from: " IS DISTINCT FROM ", - operators.is_not_distinct_from: " IS NOT DISTINCT FROM ", - operators.concat_op: " || ", - operators.match_op: " MATCH ", - operators.not_match_op: " NOT MATCH ", - operators.in_op: " IN ", - operators.not_in_op: " NOT IN ", - operators.comma_op: ", ", - operators.from_: " FROM ", - operators.as_: " AS ", - operators.is_: " IS ", - operators.is_not: " IS NOT ", - operators.collate: " COLLATE ", - # unary - operators.exists: "EXISTS ", - operators.distinct_op: "DISTINCT ", - operators.inv: "NOT ", - operators.any_op: "ANY ", - operators.all_op: "ALL ", - # modifiers - operators.desc_op: " DESC", - operators.asc_op: " ASC", - operators.nulls_first_op: " NULLS FIRST", - operators.nulls_last_op: " NULLS LAST", - # bitwise - operators.bitwise_xor_op: " ^ ", - operators.bitwise_or_op: " | ", - operators.bitwise_and_op: " & ", - operators.bitwise_not_op: "~", - operators.bitwise_lshift_op: " << ", - operators.bitwise_rshift_op: " >> ", -} - -FUNCTIONS: Dict[Type[Function[Any]], str] = { - functions.coalesce: "coalesce", - functions.current_date: "CURRENT_DATE", - functions.current_time: "CURRENT_TIME", - functions.current_timestamp: "CURRENT_TIMESTAMP", - functions.current_user: "CURRENT_USER", - functions.localtime: "LOCALTIME", - functions.localtimestamp: "LOCALTIMESTAMP", - functions.random: "random", - functions.sysdate: "sysdate", - functions.session_user: "SESSION_USER", - functions.user: "USER", - functions.cube: "CUBE", - functions.rollup: "ROLLUP", - functions.grouping_sets: "GROUPING SETS", -} - - -EXTRACT_MAP = { - "month": "month", - "day": "day", - "year": "year", - "second": "second", - "hour": "hour", - "doy": "doy", - "minute": "minute", - "quarter": "quarter", - "dow": "dow", - "week": "week", - "epoch": "epoch", - "milliseconds": "milliseconds", - "microseconds": "microseconds", - "timezone_hour": "timezone_hour", - "timezone_minute": "timezone_minute", -} - -COMPOUND_KEYWORDS = { - selectable._CompoundSelectKeyword.UNION: "UNION", - selectable._CompoundSelectKeyword.UNION_ALL: "UNION ALL", - selectable._CompoundSelectKeyword.EXCEPT: "EXCEPT", - selectable._CompoundSelectKeyword.EXCEPT_ALL: "EXCEPT ALL", - selectable._CompoundSelectKeyword.INTERSECT: "INTERSECT", - selectable._CompoundSelectKeyword.INTERSECT_ALL: "INTERSECT ALL", -} - - -class ResultColumnsEntry(NamedTuple): - """Tracks a column expression that is expected to be represented - in the result rows for this statement. - - This normally refers to the columns clause of a SELECT statement - but may also refer to a RETURNING clause, as well as for dialect-specific - emulations. - - """ - - keyname: str - """string name that's expected in cursor.description""" - - name: str - """column name, may be labeled""" - - objects: Tuple[Any, ...] - """sequence of objects that should be able to locate this column - in a RowMapping. This is typically string names and aliases - as well as Column objects. - - """ - - type: TypeEngine[Any] - """Datatype to be associated with this column. This is where - the "result processing" logic directly links the compiled statement - to the rows that come back from the cursor. - - """ - - -class _ResultMapAppender(Protocol): - def __call__( - self, - keyname: str, - name: str, - objects: Sequence[Any], - type_: TypeEngine[Any], - ) -> None: ... - - -# integer indexes into ResultColumnsEntry used by cursor.py. -# some profiling showed integer access faster than named tuple -RM_RENDERED_NAME: Literal[0] = 0 -RM_NAME: Literal[1] = 1 -RM_OBJECTS: Literal[2] = 2 -RM_TYPE: Literal[3] = 3 - - -class _BaseCompilerStackEntry(TypedDict): - asfrom_froms: Set[FromClause] - correlate_froms: Set[FromClause] - selectable: ReturnsRows - - -class _CompilerStackEntry(_BaseCompilerStackEntry, total=False): - compile_state: CompileState - need_result_map_for_nested: bool - need_result_map_for_compound: bool - select_0: ReturnsRows - insert_from_select: Select[Any] - - -class ExpandedState(NamedTuple): - """represents state to use when producing "expanded" and - "post compile" bound parameters for a statement. - - "expanded" parameters are parameters that are generated at - statement execution time to suit a number of parameters passed, the most - prominent example being the individual elements inside of an IN expression. - - "post compile" parameters are parameters where the SQL literal value - will be rendered into the SQL statement at execution time, rather than - being passed as separate parameters to the driver. - - To create an :class:`.ExpandedState` instance, use the - :meth:`.SQLCompiler.construct_expanded_state` method on any - :class:`.SQLCompiler` instance. - - """ - - statement: str - """String SQL statement with parameters fully expanded""" - - parameters: _CoreSingleExecuteParams - """Parameter dictionary with parameters fully expanded. - - For a statement that uses named parameters, this dictionary will map - exactly to the names in the statement. For a statement that uses - positional parameters, the :attr:`.ExpandedState.positional_parameters` - will yield a tuple with the positional parameter set. - - """ - - processors: Mapping[str, _BindProcessorType[Any]] - """mapping of bound value processors""" - - positiontup: Optional[Sequence[str]] - """Sequence of string names indicating the order of positional - parameters""" - - parameter_expansion: Mapping[str, List[str]] - """Mapping representing the intermediary link from original parameter - name to list of "expanded" parameter names, for those parameters that - were expanded.""" - - @property - def positional_parameters(self) -> Tuple[Any, ...]: - """Tuple of positional parameters, for statements that were compiled - using a positional paramstyle. - - """ - if self.positiontup is None: - raise exc.InvalidRequestError( - "statement does not use a positional paramstyle" - ) - return tuple(self.parameters[key] for key in self.positiontup) - - @property - def additional_parameters(self) -> _CoreSingleExecuteParams: - """synonym for :attr:`.ExpandedState.parameters`.""" - return self.parameters - - -class _InsertManyValues(NamedTuple): - """represents state to use for executing an "insertmanyvalues" statement. - - The primary consumers of this object are the - :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and - :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods. - - .. versionadded:: 2.0 - - """ - - is_default_expr: bool - """if True, the statement is of the form - ``INSERT INTO TABLE DEFAULT VALUES``, and can't be rewritten as a "batch" - - """ - - single_values_expr: str - """The rendered "values" clause of the INSERT statement. - - This is typically the parenthesized section e.g. "(?, ?, ?)" or similar. - The insertmanyvalues logic uses this string as a search and replace - target. - - """ - - insert_crud_params: List[crud._CrudParamElementStr] - """List of Column / bind names etc. used while rewriting the statement""" - - num_positional_params_counted: int - """the number of bound parameters in a single-row statement. - - This count may be larger or smaller than the actual number of columns - targeted in the INSERT, as it accommodates for SQL expressions - in the values list that may have zero or more parameters embedded - within them. - - This count is part of what's used to organize rewritten parameter lists - when batching. - - """ - - sort_by_parameter_order: bool = False - """if the deterministic_returnined_order parameter were used on the - insert. - - All of the attributes following this will only be used if this is True. - - """ - - includes_upsert_behaviors: bool = False - """if True, we have to accommodate for upsert behaviors. - - This will in some cases downgrade "insertmanyvalues" that requests - deterministic ordering. - - """ - - sentinel_columns: Optional[Sequence[Column[Any]]] = None - """List of sentinel columns that were located. - - This list is only here if the INSERT asked for - sort_by_parameter_order=True, - and dialect-appropriate sentinel columns were located. - - .. versionadded:: 2.0.10 - - """ - - num_sentinel_columns: int = 0 - """how many sentinel columns are in the above list, if any. - - This is the same as - ``len(sentinel_columns) if sentinel_columns is not None else 0`` - - """ - - sentinel_param_keys: Optional[Sequence[str]] = None - """parameter str keys in each param dictionary / tuple - that would link to the client side "sentinel" values for that row, which - we can use to match up parameter sets to result rows. - - This is only present if sentinel_columns is present and the INSERT - statement actually refers to client side values for these sentinel - columns. - - .. versionadded:: 2.0.10 - - .. versionchanged:: 2.0.29 - the sequence is now string dictionary keys - only, used against the "compiled parameteters" collection before - the parameters were converted by bound parameter processors - - """ - - implicit_sentinel: bool = False - """if True, we have exactly one sentinel column and it uses a server side - value, currently has to generate an incrementing integer value. - - The dialect in question would have asserted that it supports receiving - these values back and sorting on that value as a means of guaranteeing - correlation with the incoming parameter list. - - .. versionadded:: 2.0.10 - - """ - - embed_values_counter: bool = False - """Whether to embed an incrementing integer counter in each parameter - set within the VALUES clause as parameters are batched over. - - This is only used for a specific INSERT..SELECT..VALUES..RETURNING syntax - where a subquery is used to produce value tuples. Current support - includes PostgreSQL, Microsoft SQL Server. - - .. versionadded:: 2.0.10 - - """ - - -class _InsertManyValuesBatch(NamedTuple): - """represents an individual batch SQL statement for insertmanyvalues. - - This is passed through the - :meth:`.SQLCompiler._deliver_insertmanyvalues_batches` and - :meth:`.DefaultDialect._deliver_insertmanyvalues_batches` methods out - to the :class:`.Connection` within the - :meth:`.Connection._exec_insertmany_context` method. - - .. versionadded:: 2.0.10 - - """ - - replaced_statement: str - replaced_parameters: _DBAPIAnyExecuteParams - processed_setinputsizes: Optional[_GenericSetInputSizesType] - batch: Sequence[_DBAPISingleExecuteParams] - sentinel_values: Sequence[Tuple[Any, ...]] - current_batch_size: int - batchnum: int - total_batches: int - rows_sorted: bool - is_downgraded: bool - - -class InsertmanyvaluesSentinelOpts(FastIntFlag): - """bitflag enum indicating styles of PK defaults - which can work as implicit sentinel columns - - """ - - NOT_SUPPORTED = 1 - AUTOINCREMENT = 2 - IDENTITY = 4 - SEQUENCE = 8 - - ANY_AUTOINCREMENT = AUTOINCREMENT | IDENTITY | SEQUENCE - _SUPPORTED_OR_NOT = NOT_SUPPORTED | ANY_AUTOINCREMENT - - USE_INSERT_FROM_SELECT = 16 - RENDER_SELECT_COL_CASTS = 64 - - -class CompilerState(IntEnum): - COMPILING = 0 - """statement is present, compilation phase in progress""" - - STRING_APPLIED = 1 - """statement is present, string form of the statement has been applied. - - Additional processors by subclasses may still be pending. - - """ - - NO_STATEMENT = 2 - """compiler does not have a statement to compile, is used - for method access""" - - -class Linting(IntEnum): - """represent preferences for the 'SQL linting' feature. - - this feature currently includes support for flagging cartesian products - in SQL statements. - - """ - - NO_LINTING = 0 - "Disable all linting." - - COLLECT_CARTESIAN_PRODUCTS = 1 - """Collect data on FROMs and cartesian products and gather into - 'self.from_linter'""" - - WARN_LINTING = 2 - "Emit warnings for linters that find problems" - - FROM_LINTING = COLLECT_CARTESIAN_PRODUCTS | WARN_LINTING - """Warn for cartesian products; combines COLLECT_CARTESIAN_PRODUCTS - and WARN_LINTING""" - - -NO_LINTING, COLLECT_CARTESIAN_PRODUCTS, WARN_LINTING, FROM_LINTING = tuple( - Linting -) - - -class FromLinter(collections.namedtuple("FromLinter", ["froms", "edges"])): - """represents current state for the "cartesian product" detection - feature.""" - - def lint(self, start=None): - froms = self.froms - if not froms: - return None, None - - edges = set(self.edges) - the_rest = set(froms) - - if start is not None: - start_with = start - the_rest.remove(start_with) - else: - start_with = the_rest.pop() - - stack = collections.deque([start_with]) - - while stack and the_rest: - node = stack.popleft() - the_rest.discard(node) - - # comparison of nodes in edges here is based on hash equality, as - # there are "annotated" elements that match the non-annotated ones. - # to remove the need for in-python hash() calls, use native - # containment routines (e.g. "node in edge", "edge.index(node)") - to_remove = {edge for edge in edges if node in edge} - - # appendleft the node in each edge that is not - # the one that matched. - stack.extendleft(edge[not edge.index(node)] for edge in to_remove) - edges.difference_update(to_remove) - - # FROMS left over? boom - if the_rest: - return the_rest, start_with - else: - return None, None - - def warn(self, stmt_type="SELECT"): - the_rest, start_with = self.lint() - - # FROMS left over? boom - if the_rest: - froms = the_rest - if froms: - template = ( - "{stmt_type} statement has a cartesian product between " - "FROM element(s) {froms} and " - 'FROM element "{start}". Apply join condition(s) ' - "between each element to resolve." - ) - froms_str = ", ".join( - f'"{self.froms[from_]}"' for from_ in froms - ) - message = template.format( - stmt_type=stmt_type, - froms=froms_str, - start=self.froms[start_with], - ) - - util.warn(message) - - -class Compiled: - """Represent a compiled SQL or DDL expression. - - The ``__str__`` method of the ``Compiled`` object should produce - the actual text of the statement. ``Compiled`` objects are - specific to their underlying database dialect, and also may - or may not be specific to the columns referenced within a - particular set of bind parameters. In no case should the - ``Compiled`` object be dependent on the actual values of those - bind parameters, even though it may reference those values as - defaults. - """ - - statement: Optional[ClauseElement] = None - "The statement to compile." - string: str = "" - "The string representation of the ``statement``" - - state: CompilerState - """description of the compiler's state""" - - is_sql = False - is_ddl = False - - _cached_metadata: Optional[CursorResultMetaData] = None - - _result_columns: Optional[List[ResultColumnsEntry]] = None - - schema_translate_map: Optional[SchemaTranslateMapType] = None - - execution_options: _ExecuteOptions = util.EMPTY_DICT - """ - Execution options propagated from the statement. In some cases, - sub-elements of the statement can modify these. - """ - - preparer: IdentifierPreparer - - _annotations: _AnnotationDict = util.EMPTY_DICT - - compile_state: Optional[CompileState] = None - """Optional :class:`.CompileState` object that maintains additional - state used by the compiler. - - Major executable objects such as :class:`_expression.Insert`, - :class:`_expression.Update`, :class:`_expression.Delete`, - :class:`_expression.Select` will generate this - state when compiled in order to calculate additional information about the - object. For the top level object that is to be executed, the state can be - stored here where it can also have applicability towards result set - processing. - - .. versionadded:: 1.4 - - """ - - dml_compile_state: Optional[CompileState] = None - """Optional :class:`.CompileState` assigned at the same point that - .isinsert, .isupdate, or .isdelete is assigned. - - This will normally be the same object as .compile_state, with the - exception of cases like the :class:`.ORMFromStatementCompileState` - object. - - .. versionadded:: 1.4.40 - - """ - - cache_key: Optional[CacheKey] = None - """The :class:`.CacheKey` that was generated ahead of creating this - :class:`.Compiled` object. - - This is used for routines that need access to the original - :class:`.CacheKey` instance generated when the :class:`.Compiled` - instance was first cached, typically in order to reconcile - the original list of :class:`.BindParameter` objects with a - per-statement list that's generated on each call. - - """ - - _gen_time: float - """Generation time of this :class:`.Compiled`, used for reporting - cache stats.""" - - def __init__( - self, - dialect: Dialect, - statement: Optional[ClauseElement], - schema_translate_map: Optional[SchemaTranslateMapType] = None, - render_schema_translate: bool = False, - compile_kwargs: Mapping[str, Any] = util.immutabledict(), - ): - """Construct a new :class:`.Compiled` object. - - :param dialect: :class:`.Dialect` to compile against. - - :param statement: :class:`_expression.ClauseElement` to be compiled. - - :param schema_translate_map: dictionary of schema names to be - translated when forming the resultant SQL - - .. seealso:: - - :ref:`schema_translating` - - :param compile_kwargs: additional kwargs that will be - passed to the initial call to :meth:`.Compiled.process`. - - - """ - self.dialect = dialect - self.preparer = self.dialect.identifier_preparer - if schema_translate_map: - self.schema_translate_map = schema_translate_map - self.preparer = self.preparer._with_schema_translate( - schema_translate_map - ) - - if statement is not None: - self.state = CompilerState.COMPILING - self.statement = statement - self.can_execute = statement.supports_execution - self._annotations = statement._annotations - if self.can_execute: - if TYPE_CHECKING: - assert isinstance(statement, Executable) - self.execution_options = statement._execution_options - self.string = self.process(self.statement, **compile_kwargs) - - if render_schema_translate: - self.string = self.preparer._render_schema_translates( - self.string, schema_translate_map - ) - - self.state = CompilerState.STRING_APPLIED - else: - self.state = CompilerState.NO_STATEMENT - - self._gen_time = perf_counter() - - def __init_subclass__(cls) -> None: - cls._init_compiler_cls() - return super().__init_subclass__() - - @classmethod - def _init_compiler_cls(cls): - pass - - def _execute_on_connection( - self, connection, distilled_params, execution_options - ): - if self.can_execute: - return connection._execute_compiled( - self, distilled_params, execution_options - ) - else: - raise exc.ObjectNotExecutableError(self.statement) - - def visit_unsupported_compilation(self, element, err, **kw): - raise exc.UnsupportedCompilationError(self, type(element)) from err - - @property - def sql_compiler(self): - """Return a Compiled that is capable of processing SQL expressions. - - If this compiler is one, it would likely just return 'self'. - - """ - - raise NotImplementedError() - - def process(self, obj: Visitable, **kwargs: Any) -> str: - return obj._compiler_dispatch(self, **kwargs) - - def __str__(self) -> str: - """Return the string text of the generated SQL or DDL.""" - - if self.state is CompilerState.STRING_APPLIED: - return self.string - else: - return "" - - def construct_params( - self, - params: Optional[_CoreSingleExecuteParams] = None, - extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None, - escape_names: bool = True, - ) -> Optional[_MutableCoreSingleExecuteParams]: - """Return the bind params for this compiled object. - - :param params: a dict of string/object pairs whose values will - override bind values compiled in to the - statement. - """ - - raise NotImplementedError() - - @property - def params(self): - """Return the bind params for this compiled object.""" - return self.construct_params() - - -class TypeCompiler(util.EnsureKWArg): - """Produces DDL specification for TypeEngine objects.""" - - ensure_kwarg = r"visit_\w+" - - def __init__(self, dialect: Dialect): - self.dialect = dialect - - def process(self, type_: TypeEngine[Any], **kw: Any) -> str: - if ( - type_._variant_mapping - and self.dialect.name in type_._variant_mapping - ): - type_ = type_._variant_mapping[self.dialect.name] - return type_._compiler_dispatch(self, **kw) - - def visit_unsupported_compilation( - self, element: Any, err: Exception, **kw: Any - ) -> NoReturn: - raise exc.UnsupportedCompilationError(self, element) from err - - -# this was a Visitable, but to allow accurate detection of -# column elements this is actually a column element -class _CompileLabel( - roles.BinaryElementRole[Any], elements.CompilerColumnElement -): - """lightweight label object which acts as an expression.Label.""" - - __visit_name__ = "label" - __slots__ = "element", "name", "_alt_names" - - def __init__(self, col, name, alt_names=()): - self.element = col - self.name = name - self._alt_names = (col,) + alt_names - - @property - def proxy_set(self): - return self.element.proxy_set - - @property - def type(self): - return self.element.type - - def self_group(self, **kw): - return self - - -class ilike_case_insensitive( - roles.BinaryElementRole[Any], elements.CompilerColumnElement -): - """produce a wrapping element for a case-insensitive portion of - an ILIKE construct. - - The construct usually renders the ``lower()`` function, but on - PostgreSQL will pass silently with the assumption that "ILIKE" - is being used. - - .. versionadded:: 2.0 - - """ - - __visit_name__ = "ilike_case_insensitive_operand" - __slots__ = "element", "comparator" - - def __init__(self, element): - self.element = element - self.comparator = element.comparator - - @property - def proxy_set(self): - return self.element.proxy_set - - @property - def type(self): - return self.element.type - - def self_group(self, **kw): - return self - - def _with_binary_element_type(self, type_): - return ilike_case_insensitive( - self.element._with_binary_element_type(type_) - ) - - -class SQLCompiler(Compiled): - """Default implementation of :class:`.Compiled`. - - Compiles :class:`_expression.ClauseElement` objects into SQL strings. - - """ - - extract_map = EXTRACT_MAP - - bindname_escape_characters: ClassVar[Mapping[str, str]] = ( - util.immutabledict( - { - "%": "P", - "(": "A", - ")": "Z", - ":": "C", - ".": "_", - "[": "_", - "]": "_", - " ": "_", - } - ) - ) - """A mapping (e.g. dict or similar) containing a lookup of - characters keyed to replacement characters which will be applied to all - 'bind names' used in SQL statements as a form of 'escaping'; the given - characters are replaced entirely with the 'replacement' character when - rendered in the SQL statement, and a similar translation is performed - on the incoming names used in parameter dictionaries passed to methods - like :meth:`_engine.Connection.execute`. - - This allows bound parameter names used in :func:`_sql.bindparam` and - other constructs to have any arbitrary characters present without any - concern for characters that aren't allowed at all on the target database. - - Third party dialects can establish their own dictionary here to replace the - default mapping, which will ensure that the particular characters in the - mapping will never appear in a bound parameter name. - - The dictionary is evaluated at **class creation time**, so cannot be - modified at runtime; it must be present on the class when the class - is first declared. - - Note that for dialects that have additional bound parameter rules such - as additional restrictions on leading characters, the - :meth:`_sql.SQLCompiler.bindparam_string` method may need to be augmented. - See the cx_Oracle compiler for an example of this. - - .. versionadded:: 2.0.0rc1 - - """ - - _bind_translate_re: ClassVar[Pattern[str]] - _bind_translate_chars: ClassVar[Mapping[str, str]] - - is_sql = True - - compound_keywords = COMPOUND_KEYWORDS - - isdelete: bool = False - isinsert: bool = False - isupdate: bool = False - """class-level defaults which can be set at the instance - level to define if this Compiled instance represents - INSERT/UPDATE/DELETE - """ - - postfetch: Optional[List[Column[Any]]] - """list of columns that can be post-fetched after INSERT or UPDATE to - receive server-updated values""" - - insert_prefetch: Sequence[Column[Any]] = () - """list of columns for which default values should be evaluated before - an INSERT takes place""" - - update_prefetch: Sequence[Column[Any]] = () - """list of columns for which onupdate default values should be evaluated - before an UPDATE takes place""" - - implicit_returning: Optional[Sequence[ColumnElement[Any]]] = None - """list of "implicit" returning columns for a toplevel INSERT or UPDATE - statement, used to receive newly generated values of columns. - - .. versionadded:: 2.0 ``implicit_returning`` replaces the previous - ``returning`` collection, which was not a generalized RETURNING - collection and instead was in fact specific to the "implicit returning" - feature. - - """ - - isplaintext: bool = False - - binds: Dict[str, BindParameter[Any]] - """a dictionary of bind parameter keys to BindParameter instances.""" - - bind_names: Dict[BindParameter[Any], str] - """a dictionary of BindParameter instances to "compiled" names - that are actually present in the generated SQL""" - - stack: List[_CompilerStackEntry] - """major statements such as SELECT, INSERT, UPDATE, DELETE are - tracked in this stack using an entry format.""" - - returning_precedes_values: bool = False - """set to True classwide to generate RETURNING - clauses before the VALUES or WHERE clause (i.e. MSSQL) - """ - - render_table_with_column_in_update_from: bool = False - """set to True classwide to indicate the SET clause - in a multi-table UPDATE statement should qualify - columns with the table name (i.e. MySQL only) - """ - - ansi_bind_rules: bool = False - """SQL 92 doesn't allow bind parameters to be used - in the columns clause of a SELECT, nor does it allow - ambiguous expressions like "? = ?". A compiler - subclass can set this flag to False if the target - driver/DB enforces this - """ - - bindtemplate: str - """template to render bound parameters based on paramstyle.""" - - compilation_bindtemplate: str - """template used by compiler to render parameters before positional - paramstyle application""" - - _numeric_binds_identifier_char: str - """Character that's used to as the identifier of a numerical bind param. - For example if this char is set to ``$``, numerical binds will be rendered - in the form ``$1, $2, $3``. - """ - - _result_columns: List[ResultColumnsEntry] - """relates label names in the final SQL to a tuple of local - column/label name, ColumnElement object (if any) and - TypeEngine. CursorResult uses this for type processing and - column targeting""" - - _textual_ordered_columns: bool = False - """tell the result object that the column names as rendered are important, - but they are also "ordered" vs. what is in the compiled object here. - - As of 1.4.42 this condition is only present when the statement is a - TextualSelect, e.g. text("....").columns(...), where it is required - that the columns are considered positionally and not by name. - - """ - - _ad_hoc_textual: bool = False - """tell the result that we encountered text() or '*' constructs in the - middle of the result columns, but we also have compiled columns, so - if the number of columns in cursor.description does not match how many - expressions we have, that means we can't rely on positional at all and - should match on name. - - """ - - _ordered_columns: bool = True - """ - if False, means we can't be sure the list of entries - in _result_columns is actually the rendered order. Usually - True unless using an unordered TextualSelect. - """ - - _loose_column_name_matching: bool = False - """tell the result object that the SQL statement is textual, wants to match - up to Column objects, and may be using the ._tq_label in the SELECT rather - than the base name. - - """ - - _numeric_binds: bool = False - """ - True if paramstyle is "numeric". This paramstyle is trickier than - all the others. - - """ - - _render_postcompile: bool = False - """ - whether to render out POSTCOMPILE params during the compile phase. - - This attribute is used only for end-user invocation of stmt.compile(); - it's never used for actual statement execution, where instead the - dialect internals access and render the internal postcompile structure - directly. - - """ - - _post_compile_expanded_state: Optional[ExpandedState] = None - """When render_postcompile is used, the ``ExpandedState`` used to create - the "expanded" SQL is assigned here, and then used by the ``.params`` - accessor and ``.construct_params()`` methods for their return values. - - .. versionadded:: 2.0.0rc1 - - """ - - _pre_expanded_string: Optional[str] = None - """Stores the original string SQL before 'post_compile' is applied, - for cases where 'post_compile' were used. - - """ - - _pre_expanded_positiontup: Optional[List[str]] = None - - _insertmanyvalues: Optional[_InsertManyValues] = None - - _insert_crud_params: Optional[crud._CrudParamSequence] = None - - literal_execute_params: FrozenSet[BindParameter[Any]] = frozenset() - """bindparameter objects that are rendered as literal values at statement - execution time. - - """ - - post_compile_params: FrozenSet[BindParameter[Any]] = frozenset() - """bindparameter objects that are rendered as bound parameter placeholders - at statement execution time. - - """ - - escaped_bind_names: util.immutabledict[str, str] = util.EMPTY_DICT - """Late escaping of bound parameter names that has to be converted - to the original name when looking in the parameter dictionary. - - """ - - has_out_parameters = False - """if True, there are bindparam() objects that have the isoutparam - flag set.""" - - postfetch_lastrowid = False - """if True, and this in insert, use cursor.lastrowid to populate - result.inserted_primary_key. """ - - _cache_key_bind_match: Optional[ - Tuple[ - Dict[ - BindParameter[Any], - List[BindParameter[Any]], - ], - Dict[ - str, - BindParameter[Any], - ], - ] - ] = None - """a mapping that will relate the BindParameter object we compile - to those that are part of the extracted collection of parameters - in the cache key, if we were given a cache key. - - """ - - positiontup: Optional[List[str]] = None - """for a compiled construct that uses a positional paramstyle, will be - a sequence of strings, indicating the names of bound parameters in order. - - This is used in order to render bound parameters in their correct order, - and is combined with the :attr:`_sql.Compiled.params` dictionary to - render parameters. - - This sequence always contains the unescaped name of the parameters. - - .. seealso:: - - :ref:`faq_sql_expression_string` - includes a usage example for - debugging use cases. - - """ - _values_bindparam: Optional[List[str]] = None - - _visited_bindparam: Optional[List[str]] = None - - inline: bool = False - - ctes: Optional[MutableMapping[CTE, str]] - - # Detect same CTE references - Dict[(level, name), cte] - # Level is required for supporting nesting - ctes_by_level_name: Dict[Tuple[int, str], CTE] - - # To retrieve key/level in ctes_by_level_name - - # Dict[cte_reference, (level, cte_name, cte_opts)] - level_name_by_cte: Dict[CTE, Tuple[int, str, selectable._CTEOpts]] - - ctes_recursive: bool - - _post_compile_pattern = re.compile(r"__\[POSTCOMPILE_(\S+?)(~~.+?~~)?\]") - _pyformat_pattern = re.compile(r"%\(([^)]+?)\)s") - _positional_pattern = re.compile( - f"{_pyformat_pattern.pattern}|{_post_compile_pattern.pattern}" - ) - - @classmethod - def _init_compiler_cls(cls): - cls._init_bind_translate() - - @classmethod - def _init_bind_translate(cls): - reg = re.escape("".join(cls.bindname_escape_characters)) - cls._bind_translate_re = re.compile(f"[{reg}]") - cls._bind_translate_chars = cls.bindname_escape_characters - - def __init__( - self, - dialect: Dialect, - statement: Optional[ClauseElement], - cache_key: Optional[CacheKey] = None, - column_keys: Optional[Sequence[str]] = None, - for_executemany: bool = False, - linting: Linting = NO_LINTING, - _supporting_against: Optional[SQLCompiler] = None, - **kwargs: Any, - ): - """Construct a new :class:`.SQLCompiler` object. - - :param dialect: :class:`.Dialect` to be used - - :param statement: :class:`_expression.ClauseElement` to be compiled - - :param column_keys: a list of column names to be compiled into an - INSERT or UPDATE statement. - - :param for_executemany: whether INSERT / UPDATE statements should - expect that they are to be invoked in an "executemany" style, - which may impact how the statement will be expected to return the - values of defaults and autoincrement / sequences and similar. - Depending on the backend and driver in use, support for retrieving - these values may be disabled which means SQL expressions may - be rendered inline, RETURNING may not be rendered, etc. - - :param kwargs: additional keyword arguments to be consumed by the - superclass. - - """ - self.column_keys = column_keys - - self.cache_key = cache_key - - if cache_key: - cksm = {b.key: b for b in cache_key[1]} - ckbm = {b: [b] for b in cache_key[1]} - self._cache_key_bind_match = (ckbm, cksm) - - # compile INSERT/UPDATE defaults/sequences to expect executemany - # style execution, which may mean no pre-execute of defaults, - # or no RETURNING - self.for_executemany = for_executemany - - self.linting = linting - - # a dictionary of bind parameter keys to BindParameter - # instances. - self.binds = {} - - # a dictionary of BindParameter instances to "compiled" names - # that are actually present in the generated SQL - self.bind_names = util.column_dict() - - # stack which keeps track of nested SELECT statements - self.stack = [] - - self._result_columns = [] - - # true if the paramstyle is positional - self.positional = dialect.positional - if self.positional: - self._numeric_binds = nb = dialect.paramstyle.startswith("numeric") - if nb: - self._numeric_binds_identifier_char = ( - "$" if dialect.paramstyle == "numeric_dollar" else ":" - ) - - self.compilation_bindtemplate = _pyformat_template - else: - self.compilation_bindtemplate = BIND_TEMPLATES[dialect.paramstyle] - - self.ctes = None - - self.label_length = ( - dialect.label_length or dialect.max_identifier_length - ) - - # a map which tracks "anonymous" identifiers that are created on - # the fly here - self.anon_map = prefix_anon_map() - - # a map which tracks "truncated" names based on - # dialect.label_length or dialect.max_identifier_length - self.truncated_names: Dict[Tuple[str, str], str] = {} - self._truncated_counters: Dict[str, int] = {} - - Compiled.__init__(self, dialect, statement, **kwargs) - - if self.isinsert or self.isupdate or self.isdelete: - if TYPE_CHECKING: - assert isinstance(statement, UpdateBase) - - if self.isinsert or self.isupdate: - if TYPE_CHECKING: - assert isinstance(statement, ValuesBase) - if statement._inline: - self.inline = True - elif self.for_executemany and ( - not self.isinsert - or ( - self.dialect.insert_executemany_returning - and statement._return_defaults - ) - ): - self.inline = True - - self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle] - - if _supporting_against: - self.__dict__.update( - { - k: v - for k, v in _supporting_against.__dict__.items() - if k - not in { - "state", - "dialect", - "preparer", - "positional", - "_numeric_binds", - "compilation_bindtemplate", - "bindtemplate", - } - } - ) - - if self.state is CompilerState.STRING_APPLIED: - if self.positional: - if self._numeric_binds: - self._process_numeric() - else: - self._process_positional() - - if self._render_postcompile: - parameters = self.construct_params( - escape_names=False, - _no_postcompile=True, - ) - - self._process_parameters_for_postcompile( - parameters, _populate_self=True - ) - - @property - def insert_single_values_expr(self) -> Optional[str]: - """When an INSERT is compiled with a single set of parameters inside - a VALUES expression, the string is assigned here, where it can be - used for insert batching schemes to rewrite the VALUES expression. - - .. versionadded:: 1.3.8 - - .. versionchanged:: 2.0 This collection is no longer used by - SQLAlchemy's built-in dialects, in favor of the currently - internal ``_insertmanyvalues`` collection that is used only by - :class:`.SQLCompiler`. - - """ - if self._insertmanyvalues is None: - return None - else: - return self._insertmanyvalues.single_values_expr - - @util.ro_memoized_property - def effective_returning(self) -> Optional[Sequence[ColumnElement[Any]]]: - """The effective "returning" columns for INSERT, UPDATE or DELETE. - - This is either the so-called "implicit returning" columns which are - calculated by the compiler on the fly, or those present based on what's - present in ``self.statement._returning`` (expanded into individual - columns using the ``._all_selected_columns`` attribute) i.e. those set - explicitly using the :meth:`.UpdateBase.returning` method. - - .. versionadded:: 2.0 - - """ - if self.implicit_returning: - return self.implicit_returning - elif self.statement is not None and is_dml(self.statement): - return [ - c - for c in self.statement._all_selected_columns - if is_column_element(c) - ] - - else: - return None - - @property - def returning(self): - """backwards compatibility; returns the - effective_returning collection. - - """ - return self.effective_returning - - @property - def current_executable(self): - """Return the current 'executable' that is being compiled. - - This is currently the :class:`_sql.Select`, :class:`_sql.Insert`, - :class:`_sql.Update`, :class:`_sql.Delete`, - :class:`_sql.CompoundSelect` object that is being compiled. - Specifically it's assigned to the ``self.stack`` list of elements. - - When a statement like the above is being compiled, it normally - is also assigned to the ``.statement`` attribute of the - :class:`_sql.Compiler` object. However, all SQL constructs are - ultimately nestable, and this attribute should never be consulted - by a ``visit_`` method, as it is not guaranteed to be assigned - nor guaranteed to correspond to the current statement being compiled. - - .. versionadded:: 1.3.21 - - For compatibility with previous versions, use the following - recipe:: - - statement = getattr(self, "current_executable", False) - if statement is False: - statement = self.stack[-1]["selectable"] - - For versions 1.4 and above, ensure only .current_executable - is used; the format of "self.stack" may change. - - - """ - try: - return self.stack[-1]["selectable"] - except IndexError as ie: - raise IndexError("Compiler does not have a stack entry") from ie - - @property - def prefetch(self): - return list(self.insert_prefetch) + list(self.update_prefetch) - - @util.memoized_property - def _global_attributes(self) -> Dict[Any, Any]: - return {} - - @util.memoized_instancemethod - def _init_cte_state(self) -> MutableMapping[CTE, str]: - """Initialize collections related to CTEs only if - a CTE is located, to save on the overhead of - these collections otherwise. - - """ - # collect CTEs to tack on top of a SELECT - # To store the query to print - Dict[cte, text_query] - ctes: MutableMapping[CTE, str] = util.OrderedDict() - self.ctes = ctes - - # Detect same CTE references - Dict[(level, name), cte] - # Level is required for supporting nesting - self.ctes_by_level_name = {} - - # To retrieve key/level in ctes_by_level_name - - # Dict[cte_reference, (level, cte_name, cte_opts)] - self.level_name_by_cte = {} - - self.ctes_recursive = False - - return ctes - - @contextlib.contextmanager - def _nested_result(self): - """special API to support the use case of 'nested result sets'""" - result_columns, ordered_columns = ( - self._result_columns, - self._ordered_columns, - ) - self._result_columns, self._ordered_columns = [], False - - try: - if self.stack: - entry = self.stack[-1] - entry["need_result_map_for_nested"] = True - else: - entry = None - yield self._result_columns, self._ordered_columns - finally: - if entry: - entry.pop("need_result_map_for_nested") - self._result_columns, self._ordered_columns = ( - result_columns, - ordered_columns, - ) - - def _process_positional(self): - assert not self.positiontup - assert self.state is CompilerState.STRING_APPLIED - assert not self._numeric_binds - - if self.dialect.paramstyle == "format": - placeholder = "%s" - else: - assert self.dialect.paramstyle == "qmark" - placeholder = "?" - - positions = [] - - def find_position(m: re.Match[str]) -> str: - normal_bind = m.group(1) - if normal_bind: - positions.append(normal_bind) - return placeholder - else: - # this a post-compile bind - positions.append(m.group(2)) - return m.group(0) - - self.string = re.sub( - self._positional_pattern, find_position, self.string - ) - - if self.escaped_bind_names: - reverse_escape = {v: k for k, v in self.escaped_bind_names.items()} - assert len(self.escaped_bind_names) == len(reverse_escape) - self.positiontup = [ - reverse_escape.get(name, name) for name in positions - ] - else: - self.positiontup = positions - - if self._insertmanyvalues: - positions = [] - - single_values_expr = re.sub( - self._positional_pattern, - find_position, - self._insertmanyvalues.single_values_expr, - ) - insert_crud_params = [ - ( - v[0], - v[1], - re.sub(self._positional_pattern, find_position, v[2]), - v[3], - ) - for v in self._insertmanyvalues.insert_crud_params - ] - - self._insertmanyvalues = self._insertmanyvalues._replace( - single_values_expr=single_values_expr, - insert_crud_params=insert_crud_params, - ) - - def _process_numeric(self): - assert self._numeric_binds - assert self.state is CompilerState.STRING_APPLIED - - num = 1 - param_pos: Dict[str, str] = {} - order: Iterable[str] - if self._insertmanyvalues and self._values_bindparam is not None: - # bindparams that are not in values are always placed first. - # this avoids the need of changing them when using executemany - # values () () - order = itertools.chain( - ( - name - for name in self.bind_names.values() - if name not in self._values_bindparam - ), - self.bind_names.values(), - ) - else: - order = self.bind_names.values() - - for bind_name in order: - if bind_name in param_pos: - continue - bind = self.binds[bind_name] - if ( - bind in self.post_compile_params - or bind in self.literal_execute_params - ): - # set to None to just mark the in positiontup, it will not - # be replaced below. - param_pos[bind_name] = None # type: ignore - else: - ph = f"{self._numeric_binds_identifier_char}{num}" - num += 1 - param_pos[bind_name] = ph - - self.next_numeric_pos = num - - self.positiontup = list(param_pos) - if self.escaped_bind_names: - len_before = len(param_pos) - param_pos = { - self.escaped_bind_names.get(name, name): pos - for name, pos in param_pos.items() - } - assert len(param_pos) == len_before - - # Can't use format here since % chars are not escaped. - self.string = self._pyformat_pattern.sub( - lambda m: param_pos[m.group(1)], self.string - ) - - if self._insertmanyvalues: - single_values_expr = ( - # format is ok here since single_values_expr includes only - # place-holders - self._insertmanyvalues.single_values_expr - % param_pos - ) - insert_crud_params = [ - (v[0], v[1], "%s", v[3]) - for v in self._insertmanyvalues.insert_crud_params - ] - - self._insertmanyvalues = self._insertmanyvalues._replace( - # This has the numbers (:1, :2) - single_values_expr=single_values_expr, - # The single binds are instead %s so they can be formatted - insert_crud_params=insert_crud_params, - ) - - @util.memoized_property - def _bind_processors( - self, - ) -> MutableMapping[ - str, Union[_BindProcessorType[Any], Sequence[_BindProcessorType[Any]]] - ]: - # mypy is not able to see the two value types as the above Union, - # it just sees "object". don't know how to resolve - return { - key: value # type: ignore - for key, value in ( - ( - self.bind_names[bindparam], - ( - bindparam.type._cached_bind_processor(self.dialect) - if not bindparam.type._is_tuple_type - else tuple( - elem_type._cached_bind_processor(self.dialect) - for elem_type in cast( - TupleType, bindparam.type - ).types - ) - ), - ) - for bindparam in self.bind_names - ) - if value is not None - } - - def is_subquery(self): - return len(self.stack) > 1 - - @property - def sql_compiler(self): - return self - - def construct_expanded_state( - self, - params: Optional[_CoreSingleExecuteParams] = None, - escape_names: bool = True, - ) -> ExpandedState: - """Return a new :class:`.ExpandedState` for a given parameter set. - - For queries that use "expanding" or other late-rendered parameters, - this method will provide for both the finalized SQL string as well - as the parameters that would be used for a particular parameter set. - - .. versionadded:: 2.0.0rc1 - - """ - parameters = self.construct_params( - params, - escape_names=escape_names, - _no_postcompile=True, - ) - return self._process_parameters_for_postcompile( - parameters, - ) - - def construct_params( - self, - params: Optional[_CoreSingleExecuteParams] = None, - extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None, - escape_names: bool = True, - _group_number: Optional[int] = None, - _check: bool = True, - _no_postcompile: bool = False, - ) -> _MutableCoreSingleExecuteParams: - """return a dictionary of bind parameter keys and values""" - - if self._render_postcompile and not _no_postcompile: - assert self._post_compile_expanded_state is not None - if not params: - return dict(self._post_compile_expanded_state.parameters) - else: - raise exc.InvalidRequestError( - "can't construct new parameters when render_postcompile " - "is used; the statement is hard-linked to the original " - "parameters. Use construct_expanded_state to generate a " - "new statement and parameters." - ) - - has_escaped_names = escape_names and bool(self.escaped_bind_names) - - if extracted_parameters: - # related the bound parameters collected in the original cache key - # to those collected in the incoming cache key. They will not have - # matching names but they will line up positionally in the same - # way. The parameters present in self.bind_names may be clones of - # these original cache key params in the case of DML but the .key - # will be guaranteed to match. - if self.cache_key is None: - raise exc.CompileError( - "This compiled object has no original cache key; " - "can't pass extracted_parameters to construct_params" - ) - else: - orig_extracted = self.cache_key[1] - - ckbm_tuple = self._cache_key_bind_match - assert ckbm_tuple is not None - ckbm, _ = ckbm_tuple - resolved_extracted = { - bind: extracted - for b, extracted in zip(orig_extracted, extracted_parameters) - for bind in ckbm[b] - } - else: - resolved_extracted = None - - if params: - pd = {} - for bindparam, name in self.bind_names.items(): - escaped_name = ( - self.escaped_bind_names.get(name, name) - if has_escaped_names - else name - ) - - if bindparam.key in params: - pd[escaped_name] = params[bindparam.key] - elif name in params: - pd[escaped_name] = params[name] - - elif _check and bindparam.required: - if _group_number: - raise exc.InvalidRequestError( - "A value is required for bind parameter %r, " - "in parameter group %d" - % (bindparam.key, _group_number), - code="cd3x", - ) - else: - raise exc.InvalidRequestError( - "A value is required for bind parameter %r" - % bindparam.key, - code="cd3x", - ) - else: - if resolved_extracted: - value_param = resolved_extracted.get( - bindparam, bindparam - ) - else: - value_param = bindparam - - if bindparam.callable: - pd[escaped_name] = value_param.effective_value - else: - pd[escaped_name] = value_param.value - return pd - else: - pd = {} - for bindparam, name in self.bind_names.items(): - escaped_name = ( - self.escaped_bind_names.get(name, name) - if has_escaped_names - else name - ) - - if _check and bindparam.required: - if _group_number: - raise exc.InvalidRequestError( - "A value is required for bind parameter %r, " - "in parameter group %d" - % (bindparam.key, _group_number), - code="cd3x", - ) - else: - raise exc.InvalidRequestError( - "A value is required for bind parameter %r" - % bindparam.key, - code="cd3x", - ) - - if resolved_extracted: - value_param = resolved_extracted.get(bindparam, bindparam) - else: - value_param = bindparam - - if bindparam.callable: - pd[escaped_name] = value_param.effective_value - else: - pd[escaped_name] = value_param.value - - return pd - - @util.memoized_instancemethod - def _get_set_input_sizes_lookup(self): - dialect = self.dialect - - include_types = dialect.include_set_input_sizes - exclude_types = dialect.exclude_set_input_sizes - - dbapi = dialect.dbapi - - def lookup_type(typ): - dbtype = typ._unwrapped_dialect_impl(dialect).get_dbapi_type(dbapi) - - if ( - dbtype is not None - and (exclude_types is None or dbtype not in exclude_types) - and (include_types is None or dbtype in include_types) - ): - return dbtype - else: - return None - - inputsizes = {} - - literal_execute_params = self.literal_execute_params - - for bindparam in self.bind_names: - if bindparam in literal_execute_params: - continue - - if bindparam.type._is_tuple_type: - inputsizes[bindparam] = [ - lookup_type(typ) - for typ in cast(TupleType, bindparam.type).types - ] - else: - inputsizes[bindparam] = lookup_type(bindparam.type) - - return inputsizes - - @property - def params(self): - """Return the bind param dictionary embedded into this - compiled object, for those values that are present. - - .. seealso:: - - :ref:`faq_sql_expression_string` - includes a usage example for - debugging use cases. - - """ - return self.construct_params(_check=False) - - def _process_parameters_for_postcompile( - self, - parameters: _MutableCoreSingleExecuteParams, - _populate_self: bool = False, - ) -> ExpandedState: - """handle special post compile parameters. - - These include: - - * "expanding" parameters -typically IN tuples that are rendered - on a per-parameter basis for an otherwise fixed SQL statement string. - - * literal_binds compiled with the literal_execute flag. Used for - things like SQL Server "TOP N" where the driver does not accommodate - N as a bound parameter. - - """ - - expanded_parameters = {} - new_positiontup: Optional[List[str]] - - pre_expanded_string = self._pre_expanded_string - if pre_expanded_string is None: - pre_expanded_string = self.string - - if self.positional: - new_positiontup = [] - - pre_expanded_positiontup = self._pre_expanded_positiontup - if pre_expanded_positiontup is None: - pre_expanded_positiontup = self.positiontup - - else: - new_positiontup = pre_expanded_positiontup = None - - processors = self._bind_processors - single_processors = cast( - "Mapping[str, _BindProcessorType[Any]]", processors - ) - tuple_processors = cast( - "Mapping[str, Sequence[_BindProcessorType[Any]]]", processors - ) - - new_processors: Dict[str, _BindProcessorType[Any]] = {} - - replacement_expressions: Dict[str, Any] = {} - to_update_sets: Dict[str, Any] = {} - - # notes: - # *unescaped* parameter names in: - # self.bind_names, self.binds, self._bind_processors, self.positiontup - # - # *escaped* parameter names in: - # construct_params(), replacement_expressions - - numeric_positiontup: Optional[List[str]] = None - - if self.positional and pre_expanded_positiontup is not None: - names: Iterable[str] = pre_expanded_positiontup - if self._numeric_binds: - numeric_positiontup = [] - else: - names = self.bind_names.values() - - ebn = self.escaped_bind_names - for name in names: - escaped_name = ebn.get(name, name) if ebn else name - parameter = self.binds[name] - - if parameter in self.literal_execute_params: - if escaped_name not in replacement_expressions: - replacement_expressions[escaped_name] = ( - self.render_literal_bindparam( - parameter, - render_literal_value=parameters.pop(escaped_name), - ) - ) - continue - - if parameter in self.post_compile_params: - if escaped_name in replacement_expressions: - to_update = to_update_sets[escaped_name] - values = None - else: - # we are removing the parameter from parameters - # because it is a list value, which is not expected by - # TypeEngine objects that would otherwise be asked to - # process it. the single name is being replaced with - # individual numbered parameters for each value in the - # param. - # - # note we are also inserting *escaped* parameter names - # into the given dictionary. default dialect will - # use these param names directly as they will not be - # in the escaped_bind_names dictionary. - values = parameters.pop(name) - - leep_res = self._literal_execute_expanding_parameter( - escaped_name, parameter, values - ) - (to_update, replacement_expr) = leep_res - - to_update_sets[escaped_name] = to_update - replacement_expressions[escaped_name] = replacement_expr - - if not parameter.literal_execute: - parameters.update(to_update) - if parameter.type._is_tuple_type: - assert values is not None - new_processors.update( - ( - "%s_%s_%s" % (name, i, j), - tuple_processors[name][j - 1], - ) - for i, tuple_element in enumerate(values, 1) - for j, _ in enumerate(tuple_element, 1) - if name in tuple_processors - and tuple_processors[name][j - 1] is not None - ) - else: - new_processors.update( - (key, single_processors[name]) - for key, _ in to_update - if name in single_processors - ) - if numeric_positiontup is not None: - numeric_positiontup.extend( - name for name, _ in to_update - ) - elif new_positiontup is not None: - # to_update has escaped names, but that's ok since - # these are new names, that aren't in the - # escaped_bind_names dict. - new_positiontup.extend(name for name, _ in to_update) - expanded_parameters[name] = [ - expand_key for expand_key, _ in to_update - ] - elif new_positiontup is not None: - new_positiontup.append(name) - - def process_expanding(m): - key = m.group(1) - expr = replacement_expressions[key] - - # if POSTCOMPILE included a bind_expression, render that - # around each element - if m.group(2): - tok = m.group(2).split("~~") - be_left, be_right = tok[1], tok[3] - expr = ", ".join( - "%s%s%s" % (be_left, exp, be_right) - for exp in expr.split(", ") - ) - return expr - - statement = re.sub( - self._post_compile_pattern, process_expanding, pre_expanded_string - ) - - if numeric_positiontup is not None: - assert new_positiontup is not None - param_pos = { - key: f"{self._numeric_binds_identifier_char}{num}" - for num, key in enumerate( - numeric_positiontup, self.next_numeric_pos - ) - } - # Can't use format here since % chars are not escaped. - statement = self._pyformat_pattern.sub( - lambda m: param_pos[m.group(1)], statement - ) - new_positiontup.extend(numeric_positiontup) - - expanded_state = ExpandedState( - statement, - parameters, - new_processors, - new_positiontup, - expanded_parameters, - ) - - if _populate_self: - # this is for the "render_postcompile" flag, which is not - # otherwise used internally and is for end-user debugging and - # special use cases. - self._pre_expanded_string = pre_expanded_string - self._pre_expanded_positiontup = pre_expanded_positiontup - self.string = expanded_state.statement - self.positiontup = ( - list(expanded_state.positiontup or ()) - if self.positional - else None - ) - self._post_compile_expanded_state = expanded_state - - return expanded_state - - @util.preload_module("sqlalchemy.engine.cursor") - def _create_result_map(self): - """utility method used for unit tests only.""" - cursor = util.preloaded.engine_cursor - return cursor.CursorResultMetaData._create_description_match_map( - self._result_columns - ) - - # assigned by crud.py for insert/update statements - _get_bind_name_for_col: _BindNameForColProtocol - - @util.memoized_property - def _within_exec_param_key_getter(self) -> Callable[[Any], str]: - getter = self._get_bind_name_for_col - return getter - - @util.memoized_property - @util.preload_module("sqlalchemy.engine.result") - def _inserted_primary_key_from_lastrowid_getter(self): - result = util.preloaded.engine_result - - param_key_getter = self._within_exec_param_key_getter - - assert self.compile_state is not None - statement = self.compile_state.statement - - if TYPE_CHECKING: - assert isinstance(statement, Insert) - - table = statement.table - - getters = [ - (operator.methodcaller("get", param_key_getter(col), None), col) - for col in table.primary_key - ] - - autoinc_getter = None - autoinc_col = table._autoincrement_column - if autoinc_col is not None: - # apply type post processors to the lastrowid - lastrowid_processor = autoinc_col.type._cached_result_processor( - self.dialect, None - ) - autoinc_key = param_key_getter(autoinc_col) - - # if a bind value is present for the autoincrement column - # in the parameters, we need to do the logic dictated by - # #7998; honor a non-None user-passed parameter over lastrowid. - # previously in the 1.4 series we weren't fetching lastrowid - # at all if the key were present in the parameters - if autoinc_key in self.binds: - - def _autoinc_getter(lastrowid, parameters): - param_value = parameters.get(autoinc_key, lastrowid) - if param_value is not None: - # they supplied non-None parameter, use that. - # SQLite at least is observed to return the wrong - # cursor.lastrowid for INSERT..ON CONFLICT so it - # can't be used in all cases - return param_value - else: - # use lastrowid - return lastrowid - - # work around mypy https://github.com/python/mypy/issues/14027 - autoinc_getter = _autoinc_getter - - else: - lastrowid_processor = None - - row_fn = result.result_tuple([col.key for col in table.primary_key]) - - def get(lastrowid, parameters): - """given cursor.lastrowid value and the parameters used for INSERT, - return a "row" that represents the primary key, either by - using the "lastrowid" or by extracting values from the parameters - that were sent along with the INSERT. - - """ - if lastrowid_processor is not None: - lastrowid = lastrowid_processor(lastrowid) - - if lastrowid is None: - return row_fn(getter(parameters) for getter, col in getters) - else: - return row_fn( - ( - ( - autoinc_getter(lastrowid, parameters) - if autoinc_getter is not None - else lastrowid - ) - if col is autoinc_col - else getter(parameters) - ) - for getter, col in getters - ) - - return get - - @util.memoized_property - @util.preload_module("sqlalchemy.engine.result") - def _inserted_primary_key_from_returning_getter(self): - if typing.TYPE_CHECKING: - from ..engine import result - else: - result = util.preloaded.engine_result - - assert self.compile_state is not None - statement = self.compile_state.statement - - if TYPE_CHECKING: - assert isinstance(statement, Insert) - - param_key_getter = self._within_exec_param_key_getter - table = statement.table - - returning = self.implicit_returning - assert returning is not None - ret = {col: idx for idx, col in enumerate(returning)} - - getters = cast( - "List[Tuple[Callable[[Any], Any], bool]]", - [ - ( - (operator.itemgetter(ret[col]), True) - if col in ret - else ( - operator.methodcaller( - "get", param_key_getter(col), None - ), - False, - ) - ) - for col in table.primary_key - ], - ) - - row_fn = result.result_tuple([col.key for col in table.primary_key]) - - def get(row, parameters): - return row_fn( - getter(row) if use_row else getter(parameters) - for getter, use_row in getters - ) - - return get - - def default_from(self): - """Called when a SELECT statement has no froms, and no FROM clause is - to be appended. - - Gives Oracle a chance to tack on a ``FROM DUAL`` to the string output. - - """ - return "" - - def visit_override_binds(self, override_binds, **kw): - """SQL compile the nested element of an _OverrideBinds with - bindparams swapped out. - - The _OverrideBinds is not normally expected to be compiled; it - is meant to be used when an already cached statement is to be used, - the compilation was already performed, and only the bound params should - be swapped in at execution time. - - However, there are test cases that exericise this object, and - additionally the ORM subquery loader is known to feed in expressions - which include this construct into new queries (discovered in #11173), - so it has to do the right thing at compile time as well. - - """ - - # get SQL text first - sqltext = override_binds.element._compiler_dispatch(self, **kw) - - # for a test compile that is not for caching, change binds after the - # fact. note that we don't try to - # swap the bindparam as we compile, because our element may be - # elsewhere in the statement already (e.g. a subquery or perhaps a - # CTE) and was already visited / compiled. See - # test_relationship_criteria.py -> - # test_selectinload_local_criteria_subquery - for k in override_binds.translate: - if k not in self.binds: - continue - bp = self.binds[k] - - # so this would work, just change the value of bp in place. - # but we dont want to mutate things outside. - # bp.value = override_binds.translate[bp.key] - # continue - - # instead, need to replace bp with new_bp or otherwise accommodate - # in all internal collections - new_bp = bp._with_value( - override_binds.translate[bp.key], - maintain_key=True, - required=False, - ) - - name = self.bind_names[bp] - self.binds[k] = self.binds[name] = new_bp - self.bind_names[new_bp] = name - self.bind_names.pop(bp, None) - - if bp in self.post_compile_params: - self.post_compile_params |= {new_bp} - if bp in self.literal_execute_params: - self.literal_execute_params |= {new_bp} - - ckbm_tuple = self._cache_key_bind_match - if ckbm_tuple: - ckbm, cksm = ckbm_tuple - for bp in bp._cloned_set: - if bp.key in cksm: - cb = cksm[bp.key] - ckbm[cb].append(new_bp) - - return sqltext - - def visit_grouping(self, grouping, asfrom=False, **kwargs): - return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" - - def visit_select_statement_grouping(self, grouping, **kwargs): - return "(" + grouping.element._compiler_dispatch(self, **kwargs) + ")" - - def visit_label_reference( - self, element, within_columns_clause=False, **kwargs - ): - if self.stack and self.dialect.supports_simple_order_by_label: - try: - compile_state = cast( - "Union[SelectState, CompoundSelectState]", - self.stack[-1]["compile_state"], - ) - except KeyError as ke: - raise exc.CompileError( - "Can't resolve label reference for ORDER BY / " - "GROUP BY / DISTINCT etc." - ) from ke - - ( - with_cols, - only_froms, - only_cols, - ) = compile_state._label_resolve_dict - if within_columns_clause: - resolve_dict = only_froms - else: - resolve_dict = only_cols - - # this can be None in the case that a _label_reference() - # were subject to a replacement operation, in which case - # the replacement of the Label element may have changed - # to something else like a ColumnClause expression. - order_by_elem = element.element._order_by_label_element - - if ( - order_by_elem is not None - and order_by_elem.name in resolve_dict - and order_by_elem.shares_lineage( - resolve_dict[order_by_elem.name] - ) - ): - kwargs["render_label_as_label"] = ( - element.element._order_by_label_element - ) - return self.process( - element.element, - within_columns_clause=within_columns_clause, - **kwargs, - ) - - def visit_textual_label_reference( - self, element, within_columns_clause=False, **kwargs - ): - if not self.stack: - # compiling the element outside of the context of a SELECT - return self.process(element._text_clause) - - try: - compile_state = cast( - "Union[SelectState, CompoundSelectState]", - self.stack[-1]["compile_state"], - ) - except KeyError as ke: - coercions._no_text_coercion( - element.element, - extra=( - "Can't resolve label reference for ORDER BY / " - "GROUP BY / DISTINCT etc." - ), - exc_cls=exc.CompileError, - err=ke, - ) - - with_cols, only_froms, only_cols = compile_state._label_resolve_dict - try: - if within_columns_clause: - col = only_froms[element.element] - else: - col = with_cols[element.element] - except KeyError as err: - coercions._no_text_coercion( - element.element, - extra=( - "Can't resolve label reference for ORDER BY / " - "GROUP BY / DISTINCT etc." - ), - exc_cls=exc.CompileError, - err=err, - ) - else: - kwargs["render_label_as_label"] = col - return self.process( - col, within_columns_clause=within_columns_clause, **kwargs - ) - - def visit_label( - self, - label, - add_to_result_map=None, - within_label_clause=False, - within_columns_clause=False, - render_label_as_label=None, - result_map_targets=(), - **kw, - ): - # only render labels within the columns clause - # or ORDER BY clause of a select. dialect-specific compilers - # can modify this behavior. - render_label_with_as = ( - within_columns_clause and not within_label_clause - ) - render_label_only = render_label_as_label is label - - if render_label_only or render_label_with_as: - if isinstance(label.name, elements._truncated_label): - labelname = self._truncated_identifier("colident", label.name) - else: - labelname = label.name - - if render_label_with_as: - if add_to_result_map is not None: - add_to_result_map( - labelname, - label.name, - (label, labelname) + label._alt_names + result_map_targets, - label.type, - ) - return ( - label.element._compiler_dispatch( - self, - within_columns_clause=True, - within_label_clause=True, - **kw, - ) - + OPERATORS[operators.as_] - + self.preparer.format_label(label, labelname) - ) - elif render_label_only: - return self.preparer.format_label(label, labelname) - else: - return label.element._compiler_dispatch( - self, within_columns_clause=False, **kw - ) - - def _fallback_column_name(self, column): - raise exc.CompileError( - "Cannot compile Column object until its 'name' is assigned." - ) - - def visit_lambda_element(self, element, **kw): - sql_element = element._resolved - return self.process(sql_element, **kw) - - def visit_column( - self, - column: ColumnClause[Any], - add_to_result_map: Optional[_ResultMapAppender] = None, - include_table: bool = True, - result_map_targets: Tuple[Any, ...] = (), - ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None, - **kwargs: Any, - ) -> str: - name = orig_name = column.name - if name is None: - name = self._fallback_column_name(column) - - is_literal = column.is_literal - if not is_literal and isinstance(name, elements._truncated_label): - name = self._truncated_identifier("colident", name) - - if add_to_result_map is not None: - targets = (column, name, column.key) + result_map_targets - if column._tq_label: - targets += (column._tq_label,) - - add_to_result_map(name, orig_name, targets, column.type) - - if is_literal: - # note we are not currently accommodating for - # literal_column(quoted_name('ident', True)) here - name = self.escape_literal_column(name) - else: - name = self.preparer.quote(name) - table = column.table - if table is None or not include_table or not table.named_with_column: - return name - else: - effective_schema = self.preparer.schema_for_object(table) - - if effective_schema: - schema_prefix = ( - self.preparer.quote_schema(effective_schema) + "." - ) - else: - schema_prefix = "" - - if TYPE_CHECKING: - assert isinstance(table, NamedFromClause) - tablename = table.name - - if ( - not effective_schema - and ambiguous_table_name_map - and tablename in ambiguous_table_name_map - ): - tablename = ambiguous_table_name_map[tablename] - - if isinstance(tablename, elements._truncated_label): - tablename = self._truncated_identifier("alias", tablename) - - return schema_prefix + self.preparer.quote(tablename) + "." + name - - def visit_collation(self, element, **kw): - return self.preparer.format_collation(element.collation) - - def visit_fromclause(self, fromclause, **kwargs): - return fromclause.name - - def visit_index(self, index, **kwargs): - return index.name - - def visit_typeclause(self, typeclause, **kw): - kw["type_expression"] = typeclause - kw["identifier_preparer"] = self.preparer - return self.dialect.type_compiler_instance.process( - typeclause.type, **kw - ) - - def post_process_text(self, text): - if self.preparer._double_percents: - text = text.replace("%", "%%") - return text - - def escape_literal_column(self, text): - if self.preparer._double_percents: - text = text.replace("%", "%%") - return text - - def visit_textclause(self, textclause, add_to_result_map=None, **kw): - def do_bindparam(m): - name = m.group(1) - if name in textclause._bindparams: - return self.process(textclause._bindparams[name], **kw) - else: - return self.bindparam_string(name, **kw) - - if not self.stack: - self.isplaintext = True - - if add_to_result_map: - # text() object is present in the columns clause of a - # select(). Add a no-name entry to the result map so that - # row[text()] produces a result - add_to_result_map(None, None, (textclause,), sqltypes.NULLTYPE) - - # un-escape any \:params - return BIND_PARAMS_ESC.sub( - lambda m: m.group(1), - BIND_PARAMS.sub( - do_bindparam, self.post_process_text(textclause.text) - ), - ) - - def visit_textual_select( - self, taf, compound_index=None, asfrom=False, **kw - ): - toplevel = not self.stack - entry = self._default_stack_entry if toplevel else self.stack[-1] - - new_entry: _CompilerStackEntry = { - "correlate_froms": set(), - "asfrom_froms": set(), - "selectable": taf, - } - self.stack.append(new_entry) - - if taf._independent_ctes: - self._dispatch_independent_ctes(taf, kw) - - populate_result_map = ( - toplevel - or ( - compound_index == 0 - and entry.get("need_result_map_for_compound", False) - ) - or entry.get("need_result_map_for_nested", False) - ) - - if populate_result_map: - self._ordered_columns = self._textual_ordered_columns = ( - taf.positional - ) - - # enable looser result column matching when the SQL text links to - # Column objects by name only - self._loose_column_name_matching = not taf.positional and bool( - taf.column_args - ) - - for c in taf.column_args: - self.process( - c, - within_columns_clause=True, - add_to_result_map=self._add_to_result_map, - ) - - text = self.process(taf.element, **kw) - if self.ctes: - nesting_level = len(self.stack) if not toplevel else None - text = self._render_cte_clause(nesting_level=nesting_level) + text - - self.stack.pop(-1) - - return text - - def visit_null(self, expr, **kw): - return "NULL" - - def visit_true(self, expr, **kw): - if self.dialect.supports_native_boolean: - return "true" - else: - return "1" - - def visit_false(self, expr, **kw): - if self.dialect.supports_native_boolean: - return "false" - else: - return "0" - - def _generate_delimited_list(self, elements, separator, **kw): - return separator.join( - s - for s in (c._compiler_dispatch(self, **kw) for c in elements) - if s - ) - - def _generate_delimited_and_list(self, clauses, **kw): - lcc, clauses = elements.BooleanClauseList._process_clauses_for_boolean( - operators.and_, - elements.True_._singleton, - elements.False_._singleton, - clauses, - ) - if lcc == 1: - return clauses[0]._compiler_dispatch(self, **kw) - else: - separator = OPERATORS[operators.and_] - return separator.join( - s - for s in (c._compiler_dispatch(self, **kw) for c in clauses) - if s - ) - - def visit_tuple(self, clauselist, **kw): - return "(%s)" % self.visit_clauselist(clauselist, **kw) - - def visit_clauselist(self, clauselist, **kw): - sep = clauselist.operator - if sep is None: - sep = " " - else: - sep = OPERATORS[clauselist.operator] - - return self._generate_delimited_list(clauselist.clauses, sep, **kw) - - def visit_expression_clauselist(self, clauselist, **kw): - operator_ = clauselist.operator - - disp = self._get_operator_dispatch( - operator_, "expression_clauselist", None - ) - if disp: - return disp(clauselist, operator_, **kw) - - try: - opstring = OPERATORS[operator_] - except KeyError as err: - raise exc.UnsupportedCompilationError(self, operator_) from err - else: - kw["_in_operator_expression"] = True - return self._generate_delimited_list( - clauselist.clauses, opstring, **kw - ) - - def visit_case(self, clause, **kwargs): - x = "CASE " - if clause.value is not None: - x += clause.value._compiler_dispatch(self, **kwargs) + " " - for cond, result in clause.whens: - x += ( - "WHEN " - + cond._compiler_dispatch(self, **kwargs) - + " THEN " - + result._compiler_dispatch(self, **kwargs) - + " " - ) - if clause.else_ is not None: - x += ( - "ELSE " + clause.else_._compiler_dispatch(self, **kwargs) + " " - ) - x += "END" - return x - - def visit_type_coerce(self, type_coerce, **kw): - return type_coerce.typed_expression._compiler_dispatch(self, **kw) - - def visit_cast(self, cast, **kwargs): - type_clause = cast.typeclause._compiler_dispatch(self, **kwargs) - match = re.match("(.*)( COLLATE .*)", type_clause) - return "CAST(%s AS %s)%s" % ( - cast.clause._compiler_dispatch(self, **kwargs), - match.group(1) if match else type_clause, - match.group(2) if match else "", - ) - - def _format_frame_clause(self, range_, **kw): - return "%s AND %s" % ( - ( - "UNBOUNDED PRECEDING" - if range_[0] is elements.RANGE_UNBOUNDED - else ( - "CURRENT ROW" - if range_[0] is elements.RANGE_CURRENT - else ( - "%s PRECEDING" - % ( - self.process( - elements.literal(abs(range_[0])), **kw - ), - ) - if range_[0] < 0 - else "%s FOLLOWING" - % (self.process(elements.literal(range_[0]), **kw),) - ) - ) - ), - ( - "UNBOUNDED FOLLOWING" - if range_[1] is elements.RANGE_UNBOUNDED - else ( - "CURRENT ROW" - if range_[1] is elements.RANGE_CURRENT - else ( - "%s PRECEDING" - % ( - self.process( - elements.literal(abs(range_[1])), **kw - ), - ) - if range_[1] < 0 - else "%s FOLLOWING" - % (self.process(elements.literal(range_[1]), **kw),) - ) - ) - ), - ) - - def visit_over(self, over, **kwargs): - text = over.element._compiler_dispatch(self, **kwargs) - if over.range_: - range_ = "RANGE BETWEEN %s" % self._format_frame_clause( - over.range_, **kwargs - ) - elif over.rows: - range_ = "ROWS BETWEEN %s" % self._format_frame_clause( - over.rows, **kwargs - ) - else: - range_ = None - - return "%s OVER (%s)" % ( - text, - " ".join( - [ - "%s BY %s" - % (word, clause._compiler_dispatch(self, **kwargs)) - for word, clause in ( - ("PARTITION", over.partition_by), - ("ORDER", over.order_by), - ) - if clause is not None and len(clause) - ] - + ([range_] if range_ else []) - ), - ) - - def visit_withingroup(self, withingroup, **kwargs): - return "%s WITHIN GROUP (ORDER BY %s)" % ( - withingroup.element._compiler_dispatch(self, **kwargs), - withingroup.order_by._compiler_dispatch(self, **kwargs), - ) - - def visit_funcfilter(self, funcfilter, **kwargs): - return "%s FILTER (WHERE %s)" % ( - funcfilter.func._compiler_dispatch(self, **kwargs), - funcfilter.criterion._compiler_dispatch(self, **kwargs), - ) - - def visit_extract(self, extract, **kwargs): - field = self.extract_map.get(extract.field, extract.field) - return "EXTRACT(%s FROM %s)" % ( - field, - extract.expr._compiler_dispatch(self, **kwargs), - ) - - def visit_scalar_function_column(self, element, **kw): - compiled_fn = self.visit_function(element.fn, **kw) - compiled_col = self.visit_column(element, **kw) - return "(%s).%s" % (compiled_fn, compiled_col) - - def visit_function( - self, - func: Function[Any], - add_to_result_map: Optional[_ResultMapAppender] = None, - **kwargs: Any, - ) -> str: - if add_to_result_map is not None: - add_to_result_map(func.name, func.name, (), func.type) - - disp = getattr(self, "visit_%s_func" % func.name.lower(), None) - - text: str - - if disp: - text = disp(func, **kwargs) - else: - name = FUNCTIONS.get(func._deannotate().__class__, None) - if name: - if func._has_args: - name += "%(expr)s" - else: - name = func.name - name = ( - self.preparer.quote(name) - if self.preparer._requires_quotes_illegal_chars(name) - or isinstance(name, elements.quoted_name) - else name - ) - name = name + "%(expr)s" - text = ".".join( - [ - ( - self.preparer.quote(tok) - if self.preparer._requires_quotes_illegal_chars(tok) - or isinstance(name, elements.quoted_name) - else tok - ) - for tok in func.packagenames - ] - + [name] - ) % {"expr": self.function_argspec(func, **kwargs)} - - if func._with_ordinality: - text += " WITH ORDINALITY" - return text - - def visit_next_value_func(self, next_value, **kw): - return self.visit_sequence(next_value.sequence) - - def visit_sequence(self, sequence, **kw): - raise NotImplementedError( - "Dialect '%s' does not support sequence increments." - % self.dialect.name - ) - - def function_argspec(self, func, **kwargs): - return func.clause_expr._compiler_dispatch(self, **kwargs) - - def visit_compound_select( - self, cs, asfrom=False, compound_index=None, **kwargs - ): - toplevel = not self.stack - - compile_state = cs._compile_state_factory(cs, self, **kwargs) - - if toplevel and not self.compile_state: - self.compile_state = compile_state - - compound_stmt = compile_state.statement - - entry = self._default_stack_entry if toplevel else self.stack[-1] - need_result_map = toplevel or ( - not compound_index - and entry.get("need_result_map_for_compound", False) - ) - - # indicates there is already a CompoundSelect in play - if compound_index == 0: - entry["select_0"] = cs - - self.stack.append( - { - "correlate_froms": entry["correlate_froms"], - "asfrom_froms": entry["asfrom_froms"], - "selectable": cs, - "compile_state": compile_state, - "need_result_map_for_compound": need_result_map, - } - ) - - if compound_stmt._independent_ctes: - self._dispatch_independent_ctes(compound_stmt, kwargs) - - keyword = self.compound_keywords[cs.keyword] - - text = (" " + keyword + " ").join( - ( - c._compiler_dispatch( - self, asfrom=asfrom, compound_index=i, **kwargs - ) - for i, c in enumerate(cs.selects) - ) - ) - - kwargs["include_table"] = False - text += self.group_by_clause(cs, **dict(asfrom=asfrom, **kwargs)) - text += self.order_by_clause(cs, **kwargs) - if cs._has_row_limiting_clause: - text += self._row_limit_clause(cs, **kwargs) - - if self.ctes: - nesting_level = len(self.stack) if not toplevel else None - text = ( - self._render_cte_clause( - nesting_level=nesting_level, - include_following_stack=True, - ) - + text - ) - - self.stack.pop(-1) - return text - - def _row_limit_clause(self, cs, **kwargs): - if cs._fetch_clause is not None: - return self.fetch_clause(cs, **kwargs) - else: - return self.limit_clause(cs, **kwargs) - - def _get_operator_dispatch(self, operator_, qualifier1, qualifier2): - attrname = "visit_%s_%s%s" % ( - operator_.__name__, - qualifier1, - "_" + qualifier2 if qualifier2 else "", - ) - return getattr(self, attrname, None) - - def visit_unary( - self, unary, add_to_result_map=None, result_map_targets=(), **kw - ): - if add_to_result_map is not None: - result_map_targets += (unary,) - kw["add_to_result_map"] = add_to_result_map - kw["result_map_targets"] = result_map_targets - - if unary.operator: - if unary.modifier: - raise exc.CompileError( - "Unary expression does not support operator " - "and modifier simultaneously" - ) - disp = self._get_operator_dispatch( - unary.operator, "unary", "operator" - ) - if disp: - return disp(unary, unary.operator, **kw) - else: - return self._generate_generic_unary_operator( - unary, OPERATORS[unary.operator], **kw - ) - elif unary.modifier: - disp = self._get_operator_dispatch( - unary.modifier, "unary", "modifier" - ) - if disp: - return disp(unary, unary.modifier, **kw) - else: - return self._generate_generic_unary_modifier( - unary, OPERATORS[unary.modifier], **kw - ) - else: - raise exc.CompileError( - "Unary expression has no operator or modifier" - ) - - def visit_truediv_binary(self, binary, operator, **kw): - if self.dialect.div_is_floordiv: - return ( - self.process(binary.left, **kw) - + " / " - # TODO: would need a fast cast again here, - # unless we want to use an implicit cast like "+ 0.0" - + self.process( - elements.Cast( - binary.right, - ( - binary.right.type - if binary.right.type._type_affinity - is sqltypes.Numeric - else sqltypes.Numeric() - ), - ), - **kw, - ) - ) - else: - return ( - self.process(binary.left, **kw) - + " / " - + self.process(binary.right, **kw) - ) - - def visit_floordiv_binary(self, binary, operator, **kw): - if ( - self.dialect.div_is_floordiv - and binary.right.type._type_affinity is sqltypes.Integer - ): - return ( - self.process(binary.left, **kw) - + " / " - + self.process(binary.right, **kw) - ) - else: - return "FLOOR(%s)" % ( - self.process(binary.left, **kw) - + " / " - + self.process(binary.right, **kw) - ) - - def visit_is_true_unary_operator(self, element, operator, **kw): - if ( - element._is_implicitly_boolean - or self.dialect.supports_native_boolean - ): - return self.process(element.element, **kw) - else: - return "%s = 1" % self.process(element.element, **kw) - - def visit_is_false_unary_operator(self, element, operator, **kw): - if ( - element._is_implicitly_boolean - or self.dialect.supports_native_boolean - ): - return "NOT %s" % self.process(element.element, **kw) - else: - return "%s = 0" % self.process(element.element, **kw) - - def visit_not_match_op_binary(self, binary, operator, **kw): - return "NOT %s" % self.visit_binary( - binary, override_operator=operators.match_op - ) - - def visit_not_in_op_binary(self, binary, operator, **kw): - # The brackets are required in the NOT IN operation because the empty - # case is handled using the form "(col NOT IN (null) OR 1 = 1)". - # The presence of the OR makes the brackets required. - return "(%s)" % self._generate_generic_binary( - binary, OPERATORS[operator], **kw - ) - - def visit_empty_set_op_expr(self, type_, expand_op, **kw): - if expand_op is operators.not_in_op: - if len(type_) > 1: - return "(%s)) OR (1 = 1" % ( - ", ".join("NULL" for element in type_) - ) - else: - return "NULL) OR (1 = 1" - elif expand_op is operators.in_op: - if len(type_) > 1: - return "(%s)) AND (1 != 1" % ( - ", ".join("NULL" for element in type_) - ) - else: - return "NULL) AND (1 != 1" - else: - return self.visit_empty_set_expr(type_) - - def visit_empty_set_expr(self, element_types, **kw): - raise NotImplementedError( - "Dialect '%s' does not support empty set expression." - % self.dialect.name - ) - - def _literal_execute_expanding_parameter_literal_binds( - self, parameter, values, bind_expression_template=None - ): - typ_dialect_impl = parameter.type._unwrapped_dialect_impl(self.dialect) - - if not values: - # empty IN expression. note we don't need to use - # bind_expression_template here because there are no - # expressions to render. - - if typ_dialect_impl._is_tuple_type: - replacement_expression = ( - "VALUES " if self.dialect.tuple_in_values else "" - ) + self.visit_empty_set_op_expr( - parameter.type.types, parameter.expand_op - ) - - else: - replacement_expression = self.visit_empty_set_op_expr( - [parameter.type], parameter.expand_op - ) - - elif typ_dialect_impl._is_tuple_type or ( - typ_dialect_impl._isnull - and isinstance(values[0], collections_abc.Sequence) - and not isinstance(values[0], (str, bytes)) - ): - if typ_dialect_impl._has_bind_expression: - raise NotImplementedError( - "bind_expression() on TupleType not supported with " - "literal_binds" - ) - - replacement_expression = ( - "VALUES " if self.dialect.tuple_in_values else "" - ) + ", ".join( - "(%s)" - % ( - ", ".join( - self.render_literal_value(value, param_type) - for value, param_type in zip( - tuple_element, parameter.type.types - ) - ) - ) - for i, tuple_element in enumerate(values) - ) - else: - if bind_expression_template: - post_compile_pattern = self._post_compile_pattern - m = post_compile_pattern.search(bind_expression_template) - assert m and m.group( - 2 - ), "unexpected format for expanding parameter" - - tok = m.group(2).split("~~") - be_left, be_right = tok[1], tok[3] - replacement_expression = ", ".join( - "%s%s%s" - % ( - be_left, - self.render_literal_value(value, parameter.type), - be_right, - ) - for value in values - ) - else: - replacement_expression = ", ".join( - self.render_literal_value(value, parameter.type) - for value in values - ) - - return (), replacement_expression - - def _literal_execute_expanding_parameter(self, name, parameter, values): - if parameter.literal_execute: - return self._literal_execute_expanding_parameter_literal_binds( - parameter, values - ) - - dialect = self.dialect - typ_dialect_impl = parameter.type._unwrapped_dialect_impl(dialect) - - if self._numeric_binds: - bind_template = self.compilation_bindtemplate - else: - bind_template = self.bindtemplate - - if ( - self.dialect._bind_typing_render_casts - and typ_dialect_impl.render_bind_cast - ): - - def _render_bindtemplate(name): - return self.render_bind_cast( - parameter.type, - typ_dialect_impl, - bind_template % {"name": name}, - ) - - else: - - def _render_bindtemplate(name): - return bind_template % {"name": name} - - if not values: - to_update = [] - if typ_dialect_impl._is_tuple_type: - replacement_expression = self.visit_empty_set_op_expr( - parameter.type.types, parameter.expand_op - ) - else: - replacement_expression = self.visit_empty_set_op_expr( - [parameter.type], parameter.expand_op - ) - - elif typ_dialect_impl._is_tuple_type or ( - typ_dialect_impl._isnull - and isinstance(values[0], collections_abc.Sequence) - and not isinstance(values[0], (str, bytes)) - ): - assert not typ_dialect_impl._is_array - to_update = [ - ("%s_%s_%s" % (name, i, j), value) - for i, tuple_element in enumerate(values, 1) - for j, value in enumerate(tuple_element, 1) - ] - - replacement_expression = ( - "VALUES " if dialect.tuple_in_values else "" - ) + ", ".join( - "(%s)" - % ( - ", ".join( - _render_bindtemplate( - to_update[i * len(tuple_element) + j][0] - ) - for j, value in enumerate(tuple_element) - ) - ) - for i, tuple_element in enumerate(values) - ) - else: - to_update = [ - ("%s_%s" % (name, i), value) - for i, value in enumerate(values, 1) - ] - replacement_expression = ", ".join( - _render_bindtemplate(key) for key, value in to_update - ) - - return to_update, replacement_expression - - def visit_binary( - self, - binary, - override_operator=None, - eager_grouping=False, - from_linter=None, - lateral_from_linter=None, - **kw, - ): - if from_linter and operators.is_comparison(binary.operator): - if lateral_from_linter is not None: - enclosing_lateral = kw["enclosing_lateral"] - lateral_from_linter.edges.update( - itertools.product( - _de_clone( - binary.left._from_objects + [enclosing_lateral] - ), - _de_clone( - binary.right._from_objects + [enclosing_lateral] - ), - ) - ) - else: - from_linter.edges.update( - itertools.product( - _de_clone(binary.left._from_objects), - _de_clone(binary.right._from_objects), - ) - ) - - # don't allow "? = ?" to render - if ( - self.ansi_bind_rules - and isinstance(binary.left, elements.BindParameter) - and isinstance(binary.right, elements.BindParameter) - ): - kw["literal_execute"] = True - - operator_ = override_operator or binary.operator - disp = self._get_operator_dispatch(operator_, "binary", None) - if disp: - return disp(binary, operator_, **kw) - else: - try: - opstring = OPERATORS[operator_] - except KeyError as err: - raise exc.UnsupportedCompilationError(self, operator_) from err - else: - return self._generate_generic_binary( - binary, - opstring, - from_linter=from_linter, - lateral_from_linter=lateral_from_linter, - **kw, - ) - - def visit_function_as_comparison_op_binary(self, element, operator, **kw): - return self.process(element.sql_function, **kw) - - def visit_mod_binary(self, binary, operator, **kw): - if self.preparer._double_percents: - return ( - self.process(binary.left, **kw) - + " %% " - + self.process(binary.right, **kw) - ) - else: - return ( - self.process(binary.left, **kw) - + " % " - + self.process(binary.right, **kw) - ) - - def visit_custom_op_binary(self, element, operator, **kw): - kw["eager_grouping"] = operator.eager_grouping - return self._generate_generic_binary( - element, - " " + self.escape_literal_column(operator.opstring) + " ", - **kw, - ) - - def visit_custom_op_unary_operator(self, element, operator, **kw): - return self._generate_generic_unary_operator( - element, self.escape_literal_column(operator.opstring) + " ", **kw - ) - - def visit_custom_op_unary_modifier(self, element, operator, **kw): - return self._generate_generic_unary_modifier( - element, " " + self.escape_literal_column(operator.opstring), **kw - ) - - def _generate_generic_binary( - self, binary, opstring, eager_grouping=False, **kw - ): - _in_operator_expression = kw.get("_in_operator_expression", False) - - kw["_in_operator_expression"] = True - kw["_binary_op"] = binary.operator - text = ( - binary.left._compiler_dispatch( - self, eager_grouping=eager_grouping, **kw - ) - + opstring - + binary.right._compiler_dispatch( - self, eager_grouping=eager_grouping, **kw - ) - ) - - if _in_operator_expression and eager_grouping: - text = "(%s)" % text - return text - - def _generate_generic_unary_operator(self, unary, opstring, **kw): - return opstring + unary.element._compiler_dispatch(self, **kw) - - def _generate_generic_unary_modifier(self, unary, opstring, **kw): - return unary.element._compiler_dispatch(self, **kw) + opstring - - @util.memoized_property - def _like_percent_literal(self): - return elements.literal_column("'%'", type_=sqltypes.STRINGTYPE) - - def visit_ilike_case_insensitive_operand(self, element, **kw): - return f"lower({element.element._compiler_dispatch(self, **kw)})" - - def visit_contains_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent.concat(binary.right).concat(percent) - return self.visit_like_op_binary(binary, operator, **kw) - - def visit_not_contains_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent.concat(binary.right).concat(percent) - return self.visit_not_like_op_binary(binary, operator, **kw) - - def visit_icontains_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent.concat( - ilike_case_insensitive(binary.right) - ).concat(percent) - return self.visit_ilike_op_binary(binary, operator, **kw) - - def visit_not_icontains_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent.concat( - ilike_case_insensitive(binary.right) - ).concat(percent) - return self.visit_not_ilike_op_binary(binary, operator, **kw) - - def visit_startswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent._rconcat(binary.right) - return self.visit_like_op_binary(binary, operator, **kw) - - def visit_not_startswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent._rconcat(binary.right) - return self.visit_not_like_op_binary(binary, operator, **kw) - - def visit_istartswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent._rconcat(ilike_case_insensitive(binary.right)) - return self.visit_ilike_op_binary(binary, operator, **kw) - - def visit_not_istartswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent._rconcat(ilike_case_insensitive(binary.right)) - return self.visit_not_ilike_op_binary(binary, operator, **kw) - - def visit_endswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent.concat(binary.right) - return self.visit_like_op_binary(binary, operator, **kw) - - def visit_not_endswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.right = percent.concat(binary.right) - return self.visit_not_like_op_binary(binary, operator, **kw) - - def visit_iendswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent.concat(ilike_case_insensitive(binary.right)) - return self.visit_ilike_op_binary(binary, operator, **kw) - - def visit_not_iendswith_op_binary(self, binary, operator, **kw): - binary = binary._clone() - percent = self._like_percent_literal - binary.left = ilike_case_insensitive(binary.left) - binary.right = percent.concat(ilike_case_insensitive(binary.right)) - return self.visit_not_ilike_op_binary(binary, operator, **kw) - - def visit_like_op_binary(self, binary, operator, **kw): - escape = binary.modifiers.get("escape", None) - - return "%s LIKE %s" % ( - binary.left._compiler_dispatch(self, **kw), - binary.right._compiler_dispatch(self, **kw), - ) + ( - " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) - if escape is not None - else "" - ) - - def visit_not_like_op_binary(self, binary, operator, **kw): - escape = binary.modifiers.get("escape", None) - return "%s NOT LIKE %s" % ( - binary.left._compiler_dispatch(self, **kw), - binary.right._compiler_dispatch(self, **kw), - ) + ( - " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE) - if escape is not None - else "" - ) - - def visit_ilike_op_binary(self, binary, operator, **kw): - if operator is operators.ilike_op: - binary = binary._clone() - binary.left = ilike_case_insensitive(binary.left) - binary.right = ilike_case_insensitive(binary.right) - # else we assume ilower() has been applied - - return self.visit_like_op_binary(binary, operator, **kw) - - def visit_not_ilike_op_binary(self, binary, operator, **kw): - if operator is operators.not_ilike_op: - binary = binary._clone() - binary.left = ilike_case_insensitive(binary.left) - binary.right = ilike_case_insensitive(binary.right) - # else we assume ilower() has been applied - - return self.visit_not_like_op_binary(binary, operator, **kw) - - def visit_between_op_binary(self, binary, operator, **kw): - symmetric = binary.modifiers.get("symmetric", False) - return self._generate_generic_binary( - binary, " BETWEEN SYMMETRIC " if symmetric else " BETWEEN ", **kw - ) - - def visit_not_between_op_binary(self, binary, operator, **kw): - symmetric = binary.modifiers.get("symmetric", False) - return self._generate_generic_binary( - binary, - " NOT BETWEEN SYMMETRIC " if symmetric else " NOT BETWEEN ", - **kw, - ) - - def visit_regexp_match_op_binary(self, binary, operator, **kw): - raise exc.CompileError( - "%s dialect does not support regular expressions" - % self.dialect.name - ) - - def visit_not_regexp_match_op_binary(self, binary, operator, **kw): - raise exc.CompileError( - "%s dialect does not support regular expressions" - % self.dialect.name - ) - - def visit_regexp_replace_op_binary(self, binary, operator, **kw): - raise exc.CompileError( - "%s dialect does not support regular expression replacements" - % self.dialect.name - ) - - def visit_bindparam( - self, - bindparam, - within_columns_clause=False, - literal_binds=False, - skip_bind_expression=False, - literal_execute=False, - render_postcompile=False, - **kwargs, - ): - - if not skip_bind_expression: - impl = bindparam.type.dialect_impl(self.dialect) - if impl._has_bind_expression: - bind_expression = impl.bind_expression(bindparam) - wrapped = self.process( - bind_expression, - skip_bind_expression=True, - within_columns_clause=within_columns_clause, - literal_binds=literal_binds and not bindparam.expanding, - literal_execute=literal_execute, - render_postcompile=render_postcompile, - **kwargs, - ) - if bindparam.expanding: - # for postcompile w/ expanding, move the "wrapped" part - # of this into the inside - - m = re.match( - r"^(.*)\(__\[POSTCOMPILE_(\S+?)\]\)(.*)$", wrapped - ) - assert m, "unexpected format for expanding parameter" - wrapped = "(__[POSTCOMPILE_%s~~%s~~REPL~~%s~~])" % ( - m.group(2), - m.group(1), - m.group(3), - ) - - if literal_binds: - ret = self.render_literal_bindparam( - bindparam, - within_columns_clause=True, - bind_expression_template=wrapped, - **kwargs, - ) - return "(%s)" % ret - - return wrapped - - if not literal_binds: - literal_execute = ( - literal_execute - or bindparam.literal_execute - or (within_columns_clause and self.ansi_bind_rules) - ) - post_compile = literal_execute or bindparam.expanding - else: - post_compile = False - - if literal_binds: - ret = self.render_literal_bindparam( - bindparam, within_columns_clause=True, **kwargs - ) - if bindparam.expanding: - ret = "(%s)" % ret - return ret - - name = self._truncate_bindparam(bindparam) - - if name in self.binds: - existing = self.binds[name] - if existing is not bindparam: - if ( - (existing.unique or bindparam.unique) - and not existing.proxy_set.intersection( - bindparam.proxy_set - ) - and not existing._cloned_set.intersection( - bindparam._cloned_set - ) - ): - raise exc.CompileError( - "Bind parameter '%s' conflicts with " - "unique bind parameter of the same name" % name - ) - elif existing.expanding != bindparam.expanding: - raise exc.CompileError( - "Can't reuse bound parameter name '%s' in both " - "'expanding' (e.g. within an IN expression) and " - "non-expanding contexts. If this parameter is to " - "receive a list/array value, set 'expanding=True' on " - "it for expressions that aren't IN, otherwise use " - "a different parameter name." % (name,) - ) - elif existing._is_crud or bindparam._is_crud: - if existing._is_crud and bindparam._is_crud: - # TODO: this condition is not well understood. - # see tests in test/sql/test_update.py - raise exc.CompileError( - "Encountered unsupported case when compiling an " - "INSERT or UPDATE statement. If this is a " - "multi-table " - "UPDATE statement, please provide string-named " - "arguments to the " - "values() method with distinct names; support for " - "multi-table UPDATE statements that " - "target multiple tables for UPDATE is very " - "limited", - ) - else: - raise exc.CompileError( - f"bindparam() name '{bindparam.key}' is reserved " - "for automatic usage in the VALUES or SET " - "clause of this " - "insert/update statement. Please use a " - "name other than column name when using " - "bindparam() " - "with insert() or update() (for example, " - f"'b_{bindparam.key}')." - ) - - self.binds[bindparam.key] = self.binds[name] = bindparam - - # if we are given a cache key that we're going to match against, - # relate the bindparam here to one that is most likely present - # in the "extracted params" portion of the cache key. this is used - # to set up a positional mapping that is used to determine the - # correct parameters for a subsequent use of this compiled with - # a different set of parameter values. here, we accommodate for - # parameters that may have been cloned both before and after the cache - # key was been generated. - ckbm_tuple = self._cache_key_bind_match - - if ckbm_tuple: - ckbm, cksm = ckbm_tuple - for bp in bindparam._cloned_set: - if bp.key in cksm: - cb = cksm[bp.key] - ckbm[cb].append(bindparam) - - if bindparam.isoutparam: - self.has_out_parameters = True - - if post_compile: - if render_postcompile: - self._render_postcompile = True - - if literal_execute: - self.literal_execute_params |= {bindparam} - else: - self.post_compile_params |= {bindparam} - - ret = self.bindparam_string( - name, - post_compile=post_compile, - expanding=bindparam.expanding, - bindparam_type=bindparam.type, - **kwargs, - ) - - if bindparam.expanding: - ret = "(%s)" % ret - - return ret - - def render_bind_cast(self, type_, dbapi_type, sqltext): - raise NotImplementedError() - - def render_literal_bindparam( - self, - bindparam, - render_literal_value=NO_ARG, - bind_expression_template=None, - **kw, - ): - if render_literal_value is not NO_ARG: - value = render_literal_value - else: - if bindparam.value is None and bindparam.callable is None: - op = kw.get("_binary_op", None) - if op and op not in (operators.is_, operators.is_not): - util.warn_limited( - "Bound parameter '%s' rendering literal NULL in a SQL " - "expression; comparisons to NULL should not use " - "operators outside of 'is' or 'is not'", - (bindparam.key,), - ) - return self.process(sqltypes.NULLTYPE, **kw) - value = bindparam.effective_value - - if bindparam.expanding: - leep = self._literal_execute_expanding_parameter_literal_binds - to_update, replacement_expr = leep( - bindparam, - value, - bind_expression_template=bind_expression_template, - ) - return replacement_expr - else: - return self.render_literal_value(value, bindparam.type) - - def render_literal_value(self, value, type_): - """Render the value of a bind parameter as a quoted literal. - - This is used for statement sections that do not accept bind parameters - on the target driver/database. - - This should be implemented by subclasses using the quoting services - of the DBAPI. - - """ - - if value is None and not type_.should_evaluate_none: - # issue #10535 - handle NULL in the compiler without placing - # this onto each type, except for "evaluate None" types - # (e.g. JSON) - return self.process(elements.Null._instance()) - - processor = type_._cached_literal_processor(self.dialect) - if processor: - try: - return processor(value) - except Exception as e: - raise exc.CompileError( - f"Could not render literal value " - f'"{sql_util._repr_single_value(value)}" ' - f"with datatype " - f"{type_}; see parent stack trace for " - "more detail." - ) from e - - else: - raise exc.CompileError( - f"No literal value renderer is available for literal value " - f'"{sql_util._repr_single_value(value)}" ' - f"with datatype {type_}" - ) - - def _truncate_bindparam(self, bindparam): - if bindparam in self.bind_names: - return self.bind_names[bindparam] - - bind_name = bindparam.key - if isinstance(bind_name, elements._truncated_label): - bind_name = self._truncated_identifier("bindparam", bind_name) - - # add to bind_names for translation - self.bind_names[bindparam] = bind_name - - return bind_name - - def _truncated_identifier( - self, ident_class: str, name: _truncated_label - ) -> str: - if (ident_class, name) in self.truncated_names: - return self.truncated_names[(ident_class, name)] - - anonname = name.apply_map(self.anon_map) - - if len(anonname) > self.label_length - 6: - counter = self._truncated_counters.get(ident_class, 1) - truncname = ( - anonname[0 : max(self.label_length - 6, 0)] - + "_" - + hex(counter)[2:] - ) - self._truncated_counters[ident_class] = counter + 1 - else: - truncname = anonname - self.truncated_names[(ident_class, name)] = truncname - return truncname - - def _anonymize(self, name: str) -> str: - return name % self.anon_map - - def bindparam_string( - self, - name: str, - post_compile: bool = False, - expanding: bool = False, - escaped_from: Optional[str] = None, - bindparam_type: Optional[TypeEngine[Any]] = None, - accumulate_bind_names: Optional[Set[str]] = None, - visited_bindparam: Optional[List[str]] = None, - **kw: Any, - ) -> str: - # TODO: accumulate_bind_names is passed by crud.py to gather - # names on a per-value basis, visited_bindparam is passed by - # visit_insert() to collect all parameters in the statement. - # see if this gathering can be simplified somehow - if accumulate_bind_names is not None: - accumulate_bind_names.add(name) - if visited_bindparam is not None: - visited_bindparam.append(name) - - if not escaped_from: - if self._bind_translate_re.search(name): - # not quite the translate use case as we want to - # also get a quick boolean if we even found - # unusual characters in the name - new_name = self._bind_translate_re.sub( - lambda m: self._bind_translate_chars[m.group(0)], - name, - ) - escaped_from = name - name = new_name - - if escaped_from: - self.escaped_bind_names = self.escaped_bind_names.union( - {escaped_from: name} - ) - if post_compile: - ret = "__[POSTCOMPILE_%s]" % name - if expanding: - # for expanding, bound parameters or literal values will be - # rendered per item - return ret - - # otherwise, for non-expanding "literal execute", apply - # bind casts as determined by the datatype - if bindparam_type is not None: - type_impl = bindparam_type._unwrapped_dialect_impl( - self.dialect - ) - if type_impl.render_literal_cast: - ret = self.render_bind_cast(bindparam_type, type_impl, ret) - return ret - elif self.state is CompilerState.COMPILING: - ret = self.compilation_bindtemplate % {"name": name} - else: - ret = self.bindtemplate % {"name": name} - - if ( - bindparam_type is not None - and self.dialect._bind_typing_render_casts - ): - type_impl = bindparam_type._unwrapped_dialect_impl(self.dialect) - if type_impl.render_bind_cast: - ret = self.render_bind_cast(bindparam_type, type_impl, ret) - - return ret - - def _dispatch_independent_ctes(self, stmt, kw): - local_kw = kw.copy() - local_kw.pop("cte_opts", None) - for cte, opt in zip( - stmt._independent_ctes, stmt._independent_ctes_opts - ): - cte._compiler_dispatch(self, cte_opts=opt, **local_kw) - - def visit_cte( - self, - cte: CTE, - asfrom: bool = False, - ashint: bool = False, - fromhints: Optional[_FromHintsType] = None, - visiting_cte: Optional[CTE] = None, - from_linter: Optional[FromLinter] = None, - cte_opts: selectable._CTEOpts = selectable._CTEOpts(False), - **kwargs: Any, - ) -> Optional[str]: - self_ctes = self._init_cte_state() - assert self_ctes is self.ctes - - kwargs["visiting_cte"] = cte - - cte_name = cte.name - - if isinstance(cte_name, elements._truncated_label): - cte_name = self._truncated_identifier("alias", cte_name) - - is_new_cte = True - embedded_in_current_named_cte = False - - _reference_cte = cte._get_reference_cte() - - nesting = cte.nesting or cte_opts.nesting - - # check for CTE already encountered - if _reference_cte in self.level_name_by_cte: - cte_level, _, existing_cte_opts = self.level_name_by_cte[ - _reference_cte - ] - assert _ == cte_name - - cte_level_name = (cte_level, cte_name) - existing_cte = self.ctes_by_level_name[cte_level_name] - - # check if we are receiving it here with a specific - # "nest_here" location; if so, move it to this location - - if cte_opts.nesting: - if existing_cte_opts.nesting: - raise exc.CompileError( - "CTE is stated as 'nest_here' in " - "more than one location" - ) - - old_level_name = (cte_level, cte_name) - cte_level = len(self.stack) if nesting else 1 - cte_level_name = new_level_name = (cte_level, cte_name) - - del self.ctes_by_level_name[old_level_name] - self.ctes_by_level_name[new_level_name] = existing_cte - self.level_name_by_cte[_reference_cte] = new_level_name + ( - cte_opts, - ) - - else: - cte_level = len(self.stack) if nesting else 1 - cte_level_name = (cte_level, cte_name) - - if cte_level_name in self.ctes_by_level_name: - existing_cte = self.ctes_by_level_name[cte_level_name] - else: - existing_cte = None - - if existing_cte is not None: - embedded_in_current_named_cte = visiting_cte is existing_cte - - # we've generated a same-named CTE that we are enclosed in, - # or this is the same CTE. just return the name. - if cte is existing_cte._restates or cte is existing_cte: - is_new_cte = False - elif existing_cte is cte._restates: - # we've generated a same-named CTE that is - # enclosed in us - we take precedence, so - # discard the text for the "inner". - del self_ctes[existing_cte] - - existing_cte_reference_cte = existing_cte._get_reference_cte() - - assert existing_cte_reference_cte is _reference_cte - assert existing_cte_reference_cte is existing_cte - - del self.level_name_by_cte[existing_cte_reference_cte] - else: - # if the two CTEs are deep-copy identical, consider them - # the same, **if** they are clones, that is, they came from - # the ORM or other visit method - if ( - cte._is_clone_of is not None - or existing_cte._is_clone_of is not None - ) and cte.compare(existing_cte): - is_new_cte = False - else: - raise exc.CompileError( - "Multiple, unrelated CTEs found with " - "the same name: %r" % cte_name - ) - - if not asfrom and not is_new_cte: - return None - - if cte._cte_alias is not None: - pre_alias_cte = cte._cte_alias - cte_pre_alias_name = cte._cte_alias.name - if isinstance(cte_pre_alias_name, elements._truncated_label): - cte_pre_alias_name = self._truncated_identifier( - "alias", cte_pre_alias_name - ) - else: - pre_alias_cte = cte - cte_pre_alias_name = None - - if is_new_cte: - self.ctes_by_level_name[cte_level_name] = cte - self.level_name_by_cte[_reference_cte] = cte_level_name + ( - cte_opts, - ) - - if pre_alias_cte not in self.ctes: - self.visit_cte(pre_alias_cte, **kwargs) - - if not cte_pre_alias_name and cte not in self_ctes: - if cte.recursive: - self.ctes_recursive = True - text = self.preparer.format_alias(cte, cte_name) - if cte.recursive: - col_source = cte.element - - # TODO: can we get at the .columns_plus_names collection - # that is already (or will be?) generated for the SELECT - # rather than calling twice? - recur_cols = [ - # TODO: proxy_name is not technically safe, - # see test_cte-> - # test_with_recursive_no_name_currently_buggy. not - # clear what should be done with such a case - fallback_label_name or proxy_name - for ( - _, - proxy_name, - fallback_label_name, - c, - repeated, - ) in (col_source._generate_columns_plus_names(True)) - if not repeated - ] - - text += "(%s)" % ( - ", ".join( - self.preparer.format_label_name( - ident, anon_map=self.anon_map - ) - for ident in recur_cols - ) - ) - - assert kwargs.get("subquery", False) is False - - if not self.stack: - # toplevel, this is a stringify of the - # cte directly. just compile the inner - # the way alias() does. - return cte.element._compiler_dispatch( - self, asfrom=asfrom, **kwargs - ) - else: - prefixes = self._generate_prefixes( - cte, cte._prefixes, **kwargs - ) - inner = cte.element._compiler_dispatch( - self, asfrom=True, **kwargs - ) - - text += " AS %s\n(%s)" % (prefixes, inner) - - if cte._suffixes: - text += " " + self._generate_prefixes( - cte, cte._suffixes, **kwargs - ) - - self_ctes[cte] = text - - if asfrom: - if from_linter: - from_linter.froms[cte._de_clone()] = cte_name - - if not is_new_cte and embedded_in_current_named_cte: - return self.preparer.format_alias(cte, cte_name) - - if cte_pre_alias_name: - text = self.preparer.format_alias(cte, cte_pre_alias_name) - if self.preparer._requires_quotes(cte_name): - cte_name = self.preparer.quote(cte_name) - text += self.get_render_as_alias_suffix(cte_name) - return text - else: - return self.preparer.format_alias(cte, cte_name) - - return None - - def visit_table_valued_alias(self, element, **kw): - if element.joins_implicitly: - kw["from_linter"] = None - if element._is_lateral: - return self.visit_lateral(element, **kw) - else: - return self.visit_alias(element, **kw) - - def visit_table_valued_column(self, element, **kw): - return self.visit_column(element, **kw) - - def visit_alias( - self, - alias, - asfrom=False, - ashint=False, - iscrud=False, - fromhints=None, - subquery=False, - lateral=False, - enclosing_alias=None, - from_linter=None, - **kwargs, - ): - if lateral: - if "enclosing_lateral" not in kwargs: - # if lateral is set and enclosing_lateral is not - # present, we assume we are being called directly - # from visit_lateral() and we need to set enclosing_lateral. - assert alias._is_lateral - kwargs["enclosing_lateral"] = alias - - # for lateral objects, we track a second from_linter that is... - # lateral! to the level above us. - if ( - from_linter - and "lateral_from_linter" not in kwargs - and "enclosing_lateral" in kwargs - ): - kwargs["lateral_from_linter"] = from_linter - - if enclosing_alias is not None and enclosing_alias.element is alias: - inner = alias.element._compiler_dispatch( - self, - asfrom=asfrom, - ashint=ashint, - iscrud=iscrud, - fromhints=fromhints, - lateral=lateral, - enclosing_alias=alias, - **kwargs, - ) - if subquery and (asfrom or lateral): - inner = "(%s)" % (inner,) - return inner - else: - enclosing_alias = kwargs["enclosing_alias"] = alias - - if asfrom or ashint: - if isinstance(alias.name, elements._truncated_label): - alias_name = self._truncated_identifier("alias", alias.name) - else: - alias_name = alias.name - - if ashint: - return self.preparer.format_alias(alias, alias_name) - elif asfrom: - if from_linter: - from_linter.froms[alias._de_clone()] = alias_name - - inner = alias.element._compiler_dispatch( - self, asfrom=True, lateral=lateral, **kwargs - ) - if subquery: - inner = "(%s)" % (inner,) - - ret = inner + self.get_render_as_alias_suffix( - self.preparer.format_alias(alias, alias_name) - ) - - if alias._supports_derived_columns and alias._render_derived: - ret += "(%s)" % ( - ", ".join( - "%s%s" - % ( - self.preparer.quote(col.name), - ( - " %s" - % self.dialect.type_compiler_instance.process( - col.type, **kwargs - ) - if alias._render_derived_w_types - else "" - ), - ) - for col in alias.c - ) - ) - - if fromhints and alias in fromhints: - ret = self.format_from_hint_text( - ret, alias, fromhints[alias], iscrud - ) - - return ret - else: - # note we cancel the "subquery" flag here as well - return alias.element._compiler_dispatch( - self, lateral=lateral, **kwargs - ) - - def visit_subquery(self, subquery, **kw): - kw["subquery"] = True - return self.visit_alias(subquery, **kw) - - def visit_lateral(self, lateral_, **kw): - kw["lateral"] = True - return "LATERAL %s" % self.visit_alias(lateral_, **kw) - - def visit_tablesample(self, tablesample, asfrom=False, **kw): - text = "%s TABLESAMPLE %s" % ( - self.visit_alias(tablesample, asfrom=True, **kw), - tablesample._get_method()._compiler_dispatch(self, **kw), - ) - - if tablesample.seed is not None: - text += " REPEATABLE (%s)" % ( - tablesample.seed._compiler_dispatch(self, **kw) - ) - - return text - - def _render_values(self, element, **kw): - kw.setdefault("literal_binds", element.literal_binds) - tuples = ", ".join( - self.process( - elements.Tuple( - types=element._column_types, *elem - ).self_group(), - **kw, - ) - for chunk in element._data - for elem in chunk - ) - return f"VALUES {tuples}" - - def visit_values(self, element, asfrom=False, from_linter=None, **kw): - v = self._render_values(element, **kw) - - if element._unnamed: - name = None - elif isinstance(element.name, elements._truncated_label): - name = self._truncated_identifier("values", element.name) - else: - name = element.name - - if element._is_lateral: - lateral = "LATERAL " - else: - lateral = "" - - if asfrom: - if from_linter: - from_linter.froms[element._de_clone()] = ( - name if name is not None else "(unnamed VALUES element)" - ) - - if name: - kw["include_table"] = False - v = "%s(%s)%s (%s)" % ( - lateral, - v, - self.get_render_as_alias_suffix(self.preparer.quote(name)), - ( - ", ".join( - c._compiler_dispatch(self, **kw) - for c in element.columns - ) - ), - ) - else: - v = "%s(%s)" % (lateral, v) - return v - - def visit_scalar_values(self, element, **kw): - return f"({self._render_values(element, **kw)})" - - def get_render_as_alias_suffix(self, alias_name_text): - return " AS " + alias_name_text - - def _add_to_result_map( - self, - keyname: str, - name: str, - objects: Tuple[Any, ...], - type_: TypeEngine[Any], - ) -> None: - if keyname is None or keyname == "*": - self._ordered_columns = False - self._ad_hoc_textual = True - if type_._is_tuple_type: - raise exc.CompileError( - "Most backends don't support SELECTing " - "from a tuple() object. If this is an ORM query, " - "consider using the Bundle object." - ) - self._result_columns.append( - ResultColumnsEntry(keyname, name, objects, type_) - ) - - def _label_returning_column( - self, stmt, column, populate_result_map, column_clause_args=None, **kw - ): - """Render a column with necessary labels inside of a RETURNING clause. - - This method is provided for individual dialects in place of calling - the _label_select_column method directly, so that the two use cases - of RETURNING vs. SELECT can be disambiguated going forward. - - .. versionadded:: 1.4.21 - - """ - return self._label_select_column( - None, - column, - populate_result_map, - False, - {} if column_clause_args is None else column_clause_args, - **kw, - ) - - def _label_select_column( - self, - select, - column, - populate_result_map, - asfrom, - column_clause_args, - name=None, - proxy_name=None, - fallback_label_name=None, - within_columns_clause=True, - column_is_repeated=False, - need_column_expressions=False, - include_table=True, - ): - """produce labeled columns present in a select().""" - impl = column.type.dialect_impl(self.dialect) - - if impl._has_column_expression and ( - need_column_expressions or populate_result_map - ): - col_expr = impl.column_expression(column) - else: - col_expr = column - - if populate_result_map: - # pass an "add_to_result_map" callable into the compilation - # of embedded columns. this collects information about the - # column as it will be fetched in the result and is coordinated - # with cursor.description when the query is executed. - add_to_result_map = self._add_to_result_map - - # if the SELECT statement told us this column is a repeat, - # wrap the callable with one that prevents the addition of the - # targets - if column_is_repeated: - _add_to_result_map = add_to_result_map - - def add_to_result_map(keyname, name, objects, type_): - _add_to_result_map(keyname, name, (), type_) - - # if we redefined col_expr for type expressions, wrap the - # callable with one that adds the original column to the targets - elif col_expr is not column: - _add_to_result_map = add_to_result_map - - def add_to_result_map(keyname, name, objects, type_): - _add_to_result_map( - keyname, name, (column,) + objects, type_ - ) - - else: - add_to_result_map = None - - # this method is used by some of the dialects for RETURNING, - # which has different inputs. _label_returning_column was added - # as the better target for this now however for 1.4 we will keep - # _label_select_column directly compatible with this use case. - # these assertions right now set up the current expected inputs - assert within_columns_clause, ( - "_label_select_column is only relevant within " - "the columns clause of a SELECT or RETURNING" - ) - if isinstance(column, elements.Label): - if col_expr is not column: - result_expr = _CompileLabel( - col_expr, column.name, alt_names=(column.element,) - ) - else: - result_expr = col_expr - - elif name: - # here, _columns_plus_names has determined there's an explicit - # label name we need to use. this is the default for - # tablenames_plus_columnnames as well as when columns are being - # deduplicated on name - - assert ( - proxy_name is not None - ), "proxy_name is required if 'name' is passed" - - result_expr = _CompileLabel( - col_expr, - name, - alt_names=( - proxy_name, - # this is a hack to allow legacy result column lookups - # to work as they did before; this goes away in 2.0. - # TODO: this only seems to be tested indirectly - # via test/orm/test_deprecations.py. should be a - # resultset test for this - column._tq_label, - ), - ) - else: - # determine here whether this column should be rendered in - # a labelled context or not, as we were given no required label - # name from the caller. Here we apply heuristics based on the kind - # of SQL expression involved. - - if col_expr is not column: - # type-specific expression wrapping the given column, - # so we render a label - render_with_label = True - elif isinstance(column, elements.ColumnClause): - # table-bound column, we render its name as a label if we are - # inside of a subquery only - render_with_label = ( - asfrom - and not column.is_literal - and column.table is not None - ) - elif isinstance(column, elements.TextClause): - render_with_label = False - elif isinstance(column, elements.UnaryExpression): - render_with_label = column.wraps_column_expression or asfrom - elif ( - # general class of expressions that don't have a SQL-column - # addressible name. includes scalar selects, bind parameters, - # SQL functions, others - not isinstance(column, elements.NamedColumn) - # deeper check that indicates there's no natural "name" to - # this element, which accommodates for custom SQL constructs - # that might have a ".name" attribute (but aren't SQL - # functions) but are not implementing this more recently added - # base class. in theory the "NamedColumn" check should be - # enough, however here we seek to maintain legacy behaviors - # as well. - and column._non_anon_label is None - ): - render_with_label = True - else: - render_with_label = False - - if render_with_label: - if not fallback_label_name: - # used by the RETURNING case right now. we generate it - # here as 3rd party dialects may be referring to - # _label_select_column method directly instead of the - # just-added _label_returning_column method - assert not column_is_repeated - fallback_label_name = column._anon_name_label - - fallback_label_name = ( - elements._truncated_label(fallback_label_name) - if not isinstance( - fallback_label_name, elements._truncated_label - ) - else fallback_label_name - ) - - result_expr = _CompileLabel( - col_expr, fallback_label_name, alt_names=(proxy_name,) - ) - else: - result_expr = col_expr - - column_clause_args.update( - within_columns_clause=within_columns_clause, - add_to_result_map=add_to_result_map, - include_table=include_table, - ) - return result_expr._compiler_dispatch(self, **column_clause_args) - - def format_from_hint_text(self, sqltext, table, hint, iscrud): - hinttext = self.get_from_hint_text(table, hint) - if hinttext: - sqltext += " " + hinttext - return sqltext - - def get_select_hint_text(self, byfroms): - return None - - def get_from_hint_text(self, table, text): - return None - - def get_crud_hint_text(self, table, text): - return None - - def get_statement_hint_text(self, hint_texts): - return " ".join(hint_texts) - - _default_stack_entry: _CompilerStackEntry - - if not typing.TYPE_CHECKING: - _default_stack_entry = util.immutabledict( - [("correlate_froms", frozenset()), ("asfrom_froms", frozenset())] - ) - - def _display_froms_for_select( - self, select_stmt, asfrom, lateral=False, **kw - ): - # utility method to help external dialects - # get the correct from list for a select. - # specifically the oracle dialect needs this feature - # right now. - toplevel = not self.stack - entry = self._default_stack_entry if toplevel else self.stack[-1] - - compile_state = select_stmt._compile_state_factory(select_stmt, self) - - correlate_froms = entry["correlate_froms"] - asfrom_froms = entry["asfrom_froms"] - - if asfrom and not lateral: - froms = compile_state._get_display_froms( - explicit_correlate_froms=correlate_froms.difference( - asfrom_froms - ), - implicit_correlate_froms=(), - ) - else: - froms = compile_state._get_display_froms( - explicit_correlate_froms=correlate_froms, - implicit_correlate_froms=asfrom_froms, - ) - return froms - - translate_select_structure: Any = None - """if not ``None``, should be a callable which accepts ``(select_stmt, - **kw)`` and returns a select object. this is used for structural changes - mostly to accommodate for LIMIT/OFFSET schemes - - """ - - def visit_select( - self, - select_stmt, - asfrom=False, - insert_into=False, - fromhints=None, - compound_index=None, - select_wraps_for=None, - lateral=False, - from_linter=None, - **kwargs, - ): - assert select_wraps_for is None, ( - "SQLAlchemy 1.4 requires use of " - "the translate_select_structure hook for structural " - "translations of SELECT objects" - ) - - # initial setup of SELECT. the compile_state_factory may now - # be creating a totally different SELECT from the one that was - # passed in. for ORM use this will convert from an ORM-state - # SELECT to a regular "Core" SELECT. other composed operations - # such as computation of joins will be performed. - - kwargs["within_columns_clause"] = False - - compile_state = select_stmt._compile_state_factory( - select_stmt, self, **kwargs - ) - kwargs["ambiguous_table_name_map"] = ( - compile_state._ambiguous_table_name_map - ) - - select_stmt = compile_state.statement - - toplevel = not self.stack - - if toplevel and not self.compile_state: - self.compile_state = compile_state - - is_embedded_select = compound_index is not None or insert_into - - # translate step for Oracle, SQL Server which often need to - # restructure the SELECT to allow for LIMIT/OFFSET and possibly - # other conditions - if self.translate_select_structure: - new_select_stmt = self.translate_select_structure( - select_stmt, asfrom=asfrom, **kwargs - ) - - # if SELECT was restructured, maintain a link to the originals - # and assemble a new compile state - if new_select_stmt is not select_stmt: - compile_state_wraps_for = compile_state - select_wraps_for = select_stmt - select_stmt = new_select_stmt - - compile_state = select_stmt._compile_state_factory( - select_stmt, self, **kwargs - ) - select_stmt = compile_state.statement - - entry = self._default_stack_entry if toplevel else self.stack[-1] - - populate_result_map = need_column_expressions = ( - toplevel - or entry.get("need_result_map_for_compound", False) - or entry.get("need_result_map_for_nested", False) - ) - - # indicates there is a CompoundSelect in play and we are not the - # first select - if compound_index: - populate_result_map = False - - # this was first proposed as part of #3372; however, it is not - # reached in current tests and could possibly be an assertion - # instead. - if not populate_result_map and "add_to_result_map" in kwargs: - del kwargs["add_to_result_map"] - - froms = self._setup_select_stack( - select_stmt, compile_state, entry, asfrom, lateral, compound_index - ) - - column_clause_args = kwargs.copy() - column_clause_args.update( - {"within_label_clause": False, "within_columns_clause": False} - ) - - text = "SELECT " # we're off to a good start ! - - if select_stmt._hints: - hint_text, byfrom = self._setup_select_hints(select_stmt) - if hint_text: - text += hint_text + " " - else: - byfrom = None - - if select_stmt._independent_ctes: - self._dispatch_independent_ctes(select_stmt, kwargs) - - if select_stmt._prefixes: - text += self._generate_prefixes( - select_stmt, select_stmt._prefixes, **kwargs - ) - - text += self.get_select_precolumns(select_stmt, **kwargs) - # the actual list of columns to print in the SELECT column list. - inner_columns = [ - c - for c in [ - self._label_select_column( - select_stmt, - column, - populate_result_map, - asfrom, - column_clause_args, - name=name, - proxy_name=proxy_name, - fallback_label_name=fallback_label_name, - column_is_repeated=repeated, - need_column_expressions=need_column_expressions, - ) - for ( - name, - proxy_name, - fallback_label_name, - column, - repeated, - ) in compile_state.columns_plus_names - ] - if c is not None - ] - - if populate_result_map and select_wraps_for is not None: - # if this select was generated from translate_select, - # rewrite the targeted columns in the result map - - translate = dict( - zip( - [ - name - for ( - key, - proxy_name, - fallback_label_name, - name, - repeated, - ) in compile_state.columns_plus_names - ], - [ - name - for ( - key, - proxy_name, - fallback_label_name, - name, - repeated, - ) in compile_state_wraps_for.columns_plus_names - ], - ) - ) - - self._result_columns = [ - ResultColumnsEntry( - key, name, tuple(translate.get(o, o) for o in obj), type_ - ) - for key, name, obj, type_ in self._result_columns - ] - - text = self._compose_select_body( - text, - select_stmt, - compile_state, - inner_columns, - froms, - byfrom, - toplevel, - kwargs, - ) - - if select_stmt._statement_hints: - per_dialect = [ - ht - for (dialect_name, ht) in select_stmt._statement_hints - if dialect_name in ("*", self.dialect.name) - ] - if per_dialect: - text += " " + self.get_statement_hint_text(per_dialect) - - # In compound query, CTEs are shared at the compound level - if self.ctes and (not is_embedded_select or toplevel): - nesting_level = len(self.stack) if not toplevel else None - text = self._render_cte_clause(nesting_level=nesting_level) + text - - if select_stmt._suffixes: - text += " " + self._generate_prefixes( - select_stmt, select_stmt._suffixes, **kwargs - ) - - self.stack.pop(-1) - - return text - - def _setup_select_hints( - self, select: Select[Any] - ) -> Tuple[str, _FromHintsType]: - byfrom = { - from_: hinttext - % {"name": from_._compiler_dispatch(self, ashint=True)} - for (from_, dialect), hinttext in select._hints.items() - if dialect in ("*", self.dialect.name) - } - hint_text = self.get_select_hint_text(byfrom) - return hint_text, byfrom - - def _setup_select_stack( - self, select, compile_state, entry, asfrom, lateral, compound_index - ): - correlate_froms = entry["correlate_froms"] - asfrom_froms = entry["asfrom_froms"] - - if compound_index == 0: - entry["select_0"] = select - elif compound_index: - select_0 = entry["select_0"] - numcols = len(select_0._all_selected_columns) - - if len(compile_state.columns_plus_names) != numcols: - raise exc.CompileError( - "All selectables passed to " - "CompoundSelect must have identical numbers of " - "columns; select #%d has %d columns, select " - "#%d has %d" - % ( - 1, - numcols, - compound_index + 1, - len(select._all_selected_columns), - ) - ) - - if asfrom and not lateral: - froms = compile_state._get_display_froms( - explicit_correlate_froms=correlate_froms.difference( - asfrom_froms - ), - implicit_correlate_froms=(), - ) - else: - froms = compile_state._get_display_froms( - explicit_correlate_froms=correlate_froms, - implicit_correlate_froms=asfrom_froms, - ) - - new_correlate_froms = set(_from_objects(*froms)) - all_correlate_froms = new_correlate_froms.union(correlate_froms) - - new_entry: _CompilerStackEntry = { - "asfrom_froms": new_correlate_froms, - "correlate_froms": all_correlate_froms, - "selectable": select, - "compile_state": compile_state, - } - self.stack.append(new_entry) - - return froms - - def _compose_select_body( - self, - text, - select, - compile_state, - inner_columns, - froms, - byfrom, - toplevel, - kwargs, - ): - text += ", ".join(inner_columns) - - if self.linting & COLLECT_CARTESIAN_PRODUCTS: - from_linter = FromLinter({}, set()) - warn_linting = self.linting & WARN_LINTING - if toplevel: - self.from_linter = from_linter - else: - from_linter = None - warn_linting = False - - # adjust the whitespace for no inner columns, part of #9440, - # so that a no-col SELECT comes out as "SELECT WHERE..." or - # "SELECT FROM ...". - # while it would be better to have built the SELECT starting string - # without trailing whitespace first, then add whitespace only if inner - # cols were present, this breaks compatibility with various custom - # compilation schemes that are currently being tested. - if not inner_columns: - text = text.rstrip() - - if froms: - text += " \nFROM " - - if select._hints: - text += ", ".join( - [ - f._compiler_dispatch( - self, - asfrom=True, - fromhints=byfrom, - from_linter=from_linter, - **kwargs, - ) - for f in froms - ] - ) - else: - text += ", ".join( - [ - f._compiler_dispatch( - self, - asfrom=True, - from_linter=from_linter, - **kwargs, - ) - for f in froms - ] - ) - else: - text += self.default_from() - - if select._where_criteria: - t = self._generate_delimited_and_list( - select._where_criteria, from_linter=from_linter, **kwargs - ) - if t: - text += " \nWHERE " + t - - if warn_linting: - assert from_linter is not None - from_linter.warn() - - if select._group_by_clauses: - text += self.group_by_clause(select, **kwargs) - - if select._having_criteria: - t = self._generate_delimited_and_list( - select._having_criteria, **kwargs - ) - if t: - text += " \nHAVING " + t - - if select._order_by_clauses: - text += self.order_by_clause(select, **kwargs) - - if select._has_row_limiting_clause: - text += self._row_limit_clause(select, **kwargs) - - if select._for_update_arg is not None: - text += self.for_update_clause(select, **kwargs) - - return text - - def _generate_prefixes(self, stmt, prefixes, **kw): - clause = " ".join( - prefix._compiler_dispatch(self, **kw) - for prefix, dialect_name in prefixes - if dialect_name in (None, "*") or dialect_name == self.dialect.name - ) - if clause: - clause += " " - return clause - - def _render_cte_clause( - self, - nesting_level=None, - include_following_stack=False, - ): - """ - include_following_stack - Also render the nesting CTEs on the next stack. Useful for - SQL structures like UNION or INSERT that can wrap SELECT - statements containing nesting CTEs. - """ - if not self.ctes: - return "" - - ctes: MutableMapping[CTE, str] - - if nesting_level and nesting_level > 1: - ctes = util.OrderedDict() - for cte in list(self.ctes.keys()): - cte_level, cte_name, cte_opts = self.level_name_by_cte[ - cte._get_reference_cte() - ] - nesting = cte.nesting or cte_opts.nesting - is_rendered_level = cte_level == nesting_level or ( - include_following_stack and cte_level == nesting_level + 1 - ) - if not (nesting and is_rendered_level): - continue - - ctes[cte] = self.ctes[cte] - - else: - ctes = self.ctes - - if not ctes: - return "" - ctes_recursive = any([cte.recursive for cte in ctes]) - - cte_text = self.get_cte_preamble(ctes_recursive) + " " - cte_text += ", \n".join([txt for txt in ctes.values()]) - cte_text += "\n " - - if nesting_level and nesting_level > 1: - for cte in list(ctes.keys()): - cte_level, cte_name, cte_opts = self.level_name_by_cte[ - cte._get_reference_cte() - ] - del self.ctes[cte] - del self.ctes_by_level_name[(cte_level, cte_name)] - del self.level_name_by_cte[cte._get_reference_cte()] - - return cte_text - - def get_cte_preamble(self, recursive): - if recursive: - return "WITH RECURSIVE" - else: - return "WITH" - - def get_select_precolumns(self, select, **kw): - """Called when building a ``SELECT`` statement, position is just - before column list. - - """ - if select._distinct_on: - util.warn_deprecated( - "DISTINCT ON is currently supported only by the PostgreSQL " - "dialect. Use of DISTINCT ON for other backends is currently " - "silently ignored, however this usage is deprecated, and will " - "raise CompileError in a future release for all backends " - "that do not support this syntax.", - version="1.4", - ) - return "DISTINCT " if select._distinct else "" - - def group_by_clause(self, select, **kw): - """allow dialects to customize how GROUP BY is rendered.""" - - group_by = self._generate_delimited_list( - select._group_by_clauses, OPERATORS[operators.comma_op], **kw - ) - if group_by: - return " GROUP BY " + group_by - else: - return "" - - def order_by_clause(self, select, **kw): - """allow dialects to customize how ORDER BY is rendered.""" - - order_by = self._generate_delimited_list( - select._order_by_clauses, OPERATORS[operators.comma_op], **kw - ) - - if order_by: - return " ORDER BY " + order_by - else: - return "" - - def for_update_clause(self, select, **kw): - return " FOR UPDATE" - - def returning_clause( - self, - stmt: UpdateBase, - returning_cols: Sequence[ColumnElement[Any]], - *, - populate_result_map: bool, - **kw: Any, - ) -> str: - columns = [ - self._label_returning_column( - stmt, - column, - populate_result_map, - fallback_label_name=fallback_label_name, - column_is_repeated=repeated, - name=name, - proxy_name=proxy_name, - **kw, - ) - for ( - name, - proxy_name, - fallback_label_name, - column, - repeated, - ) in stmt._generate_columns_plus_names( - True, cols=base._select_iterables(returning_cols) - ) - ] - - return "RETURNING " + ", ".join(columns) - - def limit_clause(self, select, **kw): - text = "" - if select._limit_clause is not None: - text += "\n LIMIT " + self.process(select._limit_clause, **kw) - if select._offset_clause is not None: - if select._limit_clause is None: - text += "\n LIMIT -1" - text += " OFFSET " + self.process(select._offset_clause, **kw) - return text - - def fetch_clause( - self, - select, - fetch_clause=None, - require_offset=False, - use_literal_execute_for_simple_int=False, - **kw, - ): - if fetch_clause is None: - fetch_clause = select._fetch_clause - fetch_clause_options = select._fetch_clause_options - else: - fetch_clause_options = {"percent": False, "with_ties": False} - - text = "" - - if select._offset_clause is not None: - offset_clause = select._offset_clause - if ( - use_literal_execute_for_simple_int - and select._simple_int_clause(offset_clause) - ): - offset_clause = offset_clause.render_literal_execute() - offset_str = self.process(offset_clause, **kw) - text += "\n OFFSET %s ROWS" % offset_str - elif require_offset: - text += "\n OFFSET 0 ROWS" - - if fetch_clause is not None: - if ( - use_literal_execute_for_simple_int - and select._simple_int_clause(fetch_clause) - ): - fetch_clause = fetch_clause.render_literal_execute() - text += "\n FETCH FIRST %s%s ROWS %s" % ( - self.process(fetch_clause, **kw), - " PERCENT" if fetch_clause_options["percent"] else "", - "WITH TIES" if fetch_clause_options["with_ties"] else "ONLY", - ) - return text - - def visit_table( - self, - table, - asfrom=False, - iscrud=False, - ashint=False, - fromhints=None, - use_schema=True, - from_linter=None, - ambiguous_table_name_map=None, - **kwargs, - ): - if from_linter: - from_linter.froms[table] = table.fullname - - if asfrom or ashint: - effective_schema = self.preparer.schema_for_object(table) - - if use_schema and effective_schema: - ret = ( - self.preparer.quote_schema(effective_schema) - + "." - + self.preparer.quote(table.name) - ) - else: - ret = self.preparer.quote(table.name) - - if ( - not effective_schema - and ambiguous_table_name_map - and table.name in ambiguous_table_name_map - ): - anon_name = self._truncated_identifier( - "alias", ambiguous_table_name_map[table.name] - ) - - ret = ret + self.get_render_as_alias_suffix( - self.preparer.format_alias(None, anon_name) - ) - - if fromhints and table in fromhints: - ret = self.format_from_hint_text( - ret, table, fromhints[table], iscrud - ) - return ret - else: - return "" - - def visit_join(self, join, asfrom=False, from_linter=None, **kwargs): - if from_linter: - from_linter.edges.update( - itertools.product( - _de_clone(join.left._from_objects), - _de_clone(join.right._from_objects), - ) - ) - - if join.full: - join_type = " FULL OUTER JOIN " - elif join.isouter: - join_type = " LEFT OUTER JOIN " - else: - join_type = " JOIN " - return ( - join.left._compiler_dispatch( - self, asfrom=True, from_linter=from_linter, **kwargs - ) - + join_type - + join.right._compiler_dispatch( - self, asfrom=True, from_linter=from_linter, **kwargs - ) - + " ON " - # TODO: likely need asfrom=True here? - + join.onclause._compiler_dispatch( - self, from_linter=from_linter, **kwargs - ) - ) - - def _setup_crud_hints(self, stmt, table_text): - dialect_hints = { - table: hint_text - for (table, dialect), hint_text in stmt._hints.items() - if dialect in ("*", self.dialect.name) - } - if stmt.table in dialect_hints: - table_text = self.format_from_hint_text( - table_text, stmt.table, dialect_hints[stmt.table], True - ) - return dialect_hints, table_text - - # within the realm of "insertmanyvalues sentinel columns", - # these lookups match different kinds of Column() configurations - # to specific backend capabilities. they are broken into two - # lookups, one for autoincrement columns and the other for non - # autoincrement columns - _sentinel_col_non_autoinc_lookup = util.immutabledict( - { - _SentinelDefaultCharacterization.CLIENTSIDE: ( - InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT - ), - _SentinelDefaultCharacterization.SENTINEL_DEFAULT: ( - InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT - ), - _SentinelDefaultCharacterization.NONE: ( - InsertmanyvaluesSentinelOpts._SUPPORTED_OR_NOT - ), - _SentinelDefaultCharacterization.IDENTITY: ( - InsertmanyvaluesSentinelOpts.IDENTITY - ), - _SentinelDefaultCharacterization.SEQUENCE: ( - InsertmanyvaluesSentinelOpts.SEQUENCE - ), - } - ) - _sentinel_col_autoinc_lookup = _sentinel_col_non_autoinc_lookup.union( - { - _SentinelDefaultCharacterization.NONE: ( - InsertmanyvaluesSentinelOpts.AUTOINCREMENT - ), - } - ) - - def _get_sentinel_column_for_table( - self, table: Table - ) -> Optional[Sequence[Column[Any]]]: - """given a :class:`.Table`, return a usable sentinel column or - columns for this dialect if any. - - Return None if no sentinel columns could be identified, or raise an - error if a column was marked as a sentinel explicitly but isn't - compatible with this dialect. - - """ - - sentinel_opts = self.dialect.insertmanyvalues_implicit_sentinel - sentinel_characteristics = table._sentinel_column_characteristics - - sent_cols = sentinel_characteristics.columns - - if sent_cols is None: - return None - - if sentinel_characteristics.is_autoinc: - bitmask = self._sentinel_col_autoinc_lookup.get( - sentinel_characteristics.default_characterization, 0 - ) - else: - bitmask = self._sentinel_col_non_autoinc_lookup.get( - sentinel_characteristics.default_characterization, 0 - ) - - if sentinel_opts & bitmask: - return sent_cols - - if sentinel_characteristics.is_explicit: - # a column was explicitly marked as insert_sentinel=True, - # however it is not compatible with this dialect. they should - # not indicate this column as a sentinel if they need to include - # this dialect. - - # TODO: do we want non-primary key explicit sentinel cols - # that can gracefully degrade for some backends? - # insert_sentinel="degrade" perhaps. not for the initial release. - # I am hoping people are generally not dealing with this sentinel - # business at all. - - # if is_explicit is True, there will be only one sentinel column. - - raise exc.InvalidRequestError( - f"Column {sent_cols[0]} can't be explicitly " - "marked as a sentinel column when using the " - f"{self.dialect.name} dialect, as the " - "particular type of default generation on this column is " - "not currently compatible with this dialect's specific " - f"INSERT..RETURNING syntax which can receive the " - "server-generated value in " - "a deterministic way. To remove this error, remove " - "insert_sentinel=True from primary key autoincrement " - "columns; these columns are automatically used as " - "sentinels for supported dialects in any case." - ) - - return None - - def _deliver_insertmanyvalues_batches( - self, - statement: str, - parameters: _DBAPIMultiExecuteParams, - compiled_parameters: List[_MutableCoreSingleExecuteParams], - generic_setinputsizes: Optional[_GenericSetInputSizesType], - batch_size: int, - sort_by_parameter_order: bool, - schema_translate_map: Optional[SchemaTranslateMapType], - ) -> Iterator[_InsertManyValuesBatch]: - imv = self._insertmanyvalues - assert imv is not None - - if not imv.sentinel_param_keys: - _sentinel_from_params = None - else: - _sentinel_from_params = operator.itemgetter( - *imv.sentinel_param_keys - ) - - lenparams = len(parameters) - if imv.is_default_expr and not self.dialect.supports_default_metavalue: - # backend doesn't support - # INSERT INTO table (pk_col) VALUES (DEFAULT), (DEFAULT), ... - # at the moment this is basically SQL Server due to - # not being able to use DEFAULT for identity column - # just yield out that many single statements! still - # faster than a whole connection.execute() call ;) - # - # note we still are taking advantage of the fact that we know - # we are using RETURNING. The generalized approach of fetching - # cursor.lastrowid etc. still goes through the more heavyweight - # "ExecutionContext per statement" system as it isn't usable - # as a generic "RETURNING" approach - use_row_at_a_time = True - downgraded = False - elif not self.dialect.supports_multivalues_insert or ( - sort_by_parameter_order - and self._result_columns - and (imv.sentinel_columns is None or imv.includes_upsert_behaviors) - ): - # deterministic order was requested and the compiler could - # not organize sentinel columns for this dialect/statement. - # use row at a time - use_row_at_a_time = True - downgraded = True - else: - use_row_at_a_time = False - downgraded = False - - if use_row_at_a_time: - for batchnum, (param, compiled_param) in enumerate( - cast( - "Sequence[Tuple[_DBAPISingleExecuteParams, _MutableCoreSingleExecuteParams]]", # noqa: E501 - zip(parameters, compiled_parameters), - ), - 1, - ): - yield _InsertManyValuesBatch( - statement, - param, - generic_setinputsizes, - [param], - ( - [_sentinel_from_params(compiled_param)] - if _sentinel_from_params - else [] - ), - 1, - batchnum, - lenparams, - sort_by_parameter_order, - downgraded, - ) - return - - if schema_translate_map: - rst = functools.partial( - self.preparer._render_schema_translates, - schema_translate_map=schema_translate_map, - ) - else: - rst = None - - imv_single_values_expr = imv.single_values_expr - if rst: - imv_single_values_expr = rst(imv_single_values_expr) - - executemany_values = f"({imv_single_values_expr})" - statement = statement.replace(executemany_values, "__EXECMANY_TOKEN__") - - # Use optional insertmanyvalues_max_parameters - # to further shrink the batch size so that there are no more than - # insertmanyvalues_max_parameters params. - # Currently used by SQL Server, which limits statements to 2100 bound - # parameters (actually 2099). - max_params = self.dialect.insertmanyvalues_max_parameters - if max_params: - total_num_of_params = len(self.bind_names) - num_params_per_batch = len(imv.insert_crud_params) - num_params_outside_of_batch = ( - total_num_of_params - num_params_per_batch - ) - batch_size = min( - batch_size, - ( - (max_params - num_params_outside_of_batch) - // num_params_per_batch - ), - ) - - batches = cast("List[Sequence[Any]]", list(parameters)) - compiled_batches = cast( - "List[Sequence[Any]]", list(compiled_parameters) - ) - - processed_setinputsizes: Optional[_GenericSetInputSizesType] = None - batchnum = 1 - total_batches = lenparams // batch_size + ( - 1 if lenparams % batch_size else 0 - ) - - insert_crud_params = imv.insert_crud_params - assert insert_crud_params is not None - - if rst: - insert_crud_params = [ - (col, key, rst(expr), st) - for col, key, expr, st in insert_crud_params - ] - - escaped_bind_names: Mapping[str, str] - expand_pos_lower_index = expand_pos_upper_index = 0 - - if not self.positional: - if self.escaped_bind_names: - escaped_bind_names = self.escaped_bind_names - else: - escaped_bind_names = {} - - all_keys = set(parameters[0]) - - def apply_placeholders(keys, formatted): - for key in keys: - key = escaped_bind_names.get(key, key) - formatted = formatted.replace( - self.bindtemplate % {"name": key}, - self.bindtemplate - % {"name": f"{key}__EXECMANY_INDEX__"}, - ) - return formatted - - if imv.embed_values_counter: - imv_values_counter = ", _IMV_VALUES_COUNTER" - else: - imv_values_counter = "" - formatted_values_clause = f"""({', '.join( - apply_placeholders(bind_keys, formatted) - for _, _, formatted, bind_keys in insert_crud_params - )}{imv_values_counter})""" - - keys_to_replace = all_keys.intersection( - escaped_bind_names.get(key, key) - for _, _, _, bind_keys in insert_crud_params - for key in bind_keys - ) - base_parameters = { - key: parameters[0][key] - for key in all_keys.difference(keys_to_replace) - } - executemany_values_w_comma = "" - else: - formatted_values_clause = "" - keys_to_replace = set() - base_parameters = {} - - if imv.embed_values_counter: - executemany_values_w_comma = ( - f"({imv_single_values_expr}, _IMV_VALUES_COUNTER), " - ) - else: - executemany_values_w_comma = f"({imv_single_values_expr}), " - - all_names_we_will_expand: Set[str] = set() - for elem in imv.insert_crud_params: - all_names_we_will_expand.update(elem[3]) - - # get the start and end position in a particular list - # of parameters where we will be doing the "expanding". - # statements can have params on either side or both sides, - # given RETURNING and CTEs - if all_names_we_will_expand: - positiontup = self.positiontup - assert positiontup is not None - - all_expand_positions = { - idx - for idx, name in enumerate(positiontup) - if name in all_names_we_will_expand - } - expand_pos_lower_index = min(all_expand_positions) - expand_pos_upper_index = max(all_expand_positions) + 1 - assert ( - len(all_expand_positions) - == expand_pos_upper_index - expand_pos_lower_index - ) - - if self._numeric_binds: - escaped = re.escape(self._numeric_binds_identifier_char) - executemany_values_w_comma = re.sub( - rf"{escaped}\d+", "%s", executemany_values_w_comma - ) - - while batches: - batch = batches[0:batch_size] - compiled_batch = compiled_batches[0:batch_size] - - batches[0:batch_size] = [] - compiled_batches[0:batch_size] = [] - - if batches: - current_batch_size = batch_size - else: - current_batch_size = len(batch) - - if generic_setinputsizes: - # if setinputsizes is present, expand this collection to - # suit the batch length as well - # currently this will be mssql+pyodbc for internal dialects - processed_setinputsizes = [ - (new_key, len_, typ) - for new_key, len_, typ in ( - (f"{key}_{index}", len_, typ) - for index in range(current_batch_size) - for key, len_, typ in generic_setinputsizes - ) - ] - - replaced_parameters: Any - if self.positional: - num_ins_params = imv.num_positional_params_counted - - batch_iterator: Iterable[Sequence[Any]] - extra_params_left: Sequence[Any] - extra_params_right: Sequence[Any] - - if num_ins_params == len(batch[0]): - extra_params_left = extra_params_right = () - batch_iterator = batch - else: - extra_params_left = batch[0][:expand_pos_lower_index] - extra_params_right = batch[0][expand_pos_upper_index:] - batch_iterator = ( - b[expand_pos_lower_index:expand_pos_upper_index] - for b in batch - ) - - if imv.embed_values_counter: - expanded_values_string = ( - "".join( - executemany_values_w_comma.replace( - "_IMV_VALUES_COUNTER", str(i) - ) - for i, _ in enumerate(batch) - ) - )[:-2] - else: - expanded_values_string = ( - (executemany_values_w_comma * current_batch_size) - )[:-2] - - if self._numeric_binds and num_ins_params > 0: - # numeric will always number the parameters inside of - # VALUES (and thus order self.positiontup) to be higher - # than non-VALUES parameters, no matter where in the - # statement those non-VALUES parameters appear (this is - # ensured in _process_numeric by numbering first all - # params that are not in _values_bindparam) - # therefore all extra params are always - # on the left side and numbered lower than the VALUES - # parameters - assert not extra_params_right - - start = expand_pos_lower_index + 1 - end = num_ins_params * (current_batch_size) + start - - # need to format here, since statement may contain - # unescaped %, while values_string contains just (%s, %s) - positions = tuple( - f"{self._numeric_binds_identifier_char}{i}" - for i in range(start, end) - ) - expanded_values_string = expanded_values_string % positions - - replaced_statement = statement.replace( - "__EXECMANY_TOKEN__", expanded_values_string - ) - - replaced_parameters = tuple( - itertools.chain.from_iterable(batch_iterator) - ) - - replaced_parameters = ( - extra_params_left - + replaced_parameters - + extra_params_right - ) - - else: - replaced_values_clauses = [] - replaced_parameters = base_parameters.copy() - - for i, param in enumerate(batch): - fmv = formatted_values_clause.replace( - "EXECMANY_INDEX__", str(i) - ) - if imv.embed_values_counter: - fmv = fmv.replace("_IMV_VALUES_COUNTER", str(i)) - - replaced_values_clauses.append(fmv) - replaced_parameters.update( - {f"{key}__{i}": param[key] for key in keys_to_replace} - ) - - replaced_statement = statement.replace( - "__EXECMANY_TOKEN__", - ", ".join(replaced_values_clauses), - ) - - yield _InsertManyValuesBatch( - replaced_statement, - replaced_parameters, - processed_setinputsizes, - batch, - ( - [_sentinel_from_params(cb) for cb in compiled_batch] - if _sentinel_from_params - else [] - ), - current_batch_size, - batchnum, - total_batches, - sort_by_parameter_order, - False, - ) - batchnum += 1 - - def visit_insert( - self, insert_stmt, visited_bindparam=None, visiting_cte=None, **kw - ): - compile_state = insert_stmt._compile_state_factory( - insert_stmt, self, **kw - ) - insert_stmt = compile_state.statement - - if visiting_cte is not None: - kw["visiting_cte"] = visiting_cte - toplevel = False - else: - toplevel = not self.stack - - if toplevel: - self.isinsert = True - if not self.dml_compile_state: - self.dml_compile_state = compile_state - if not self.compile_state: - self.compile_state = compile_state - - self.stack.append( - { - "correlate_froms": set(), - "asfrom_froms": set(), - "selectable": insert_stmt, - } - ) - - counted_bindparam = 0 - - # reset any incoming "visited_bindparam" collection - visited_bindparam = None - - # for positional, insertmanyvalues needs to know how many - # bound parameters are in the VALUES sequence; there's no simple - # rule because default expressions etc. can have zero or more - # params inside them. After multiple attempts to figure this out, - # this very simplistic "count after" works and is - # likely the least amount of callcounts, though looks clumsy - if self.positional and visiting_cte is None: - # if we are inside a CTE, don't count parameters - # here since they wont be for insertmanyvalues. keep - # visited_bindparam at None so no counting happens. - # see #9173 - visited_bindparam = [] - - crud_params_struct = crud._get_crud_params( - self, - insert_stmt, - compile_state, - toplevel, - visited_bindparam=visited_bindparam, - **kw, - ) - - if self.positional and visited_bindparam is not None: - counted_bindparam = len(visited_bindparam) - if self._numeric_binds: - if self._values_bindparam is not None: - self._values_bindparam += visited_bindparam - else: - self._values_bindparam = visited_bindparam - - crud_params_single = crud_params_struct.single_params - - if ( - not crud_params_single - and not self.dialect.supports_default_values - and not self.dialect.supports_default_metavalue - and not self.dialect.supports_empty_insert - ): - raise exc.CompileError( - "The '%s' dialect with current database " - "version settings does not support empty " - "inserts." % self.dialect.name - ) - - if compile_state._has_multi_parameters: - if not self.dialect.supports_multivalues_insert: - raise exc.CompileError( - "The '%s' dialect with current database " - "version settings does not support " - "in-place multirow inserts." % self.dialect.name - ) - elif ( - self.implicit_returning or insert_stmt._returning - ) and insert_stmt._sort_by_parameter_order: - raise exc.CompileError( - "RETURNING cannot be determinstically sorted when " - "using an INSERT which includes multi-row values()." - ) - crud_params_single = crud_params_struct.single_params - else: - crud_params_single = crud_params_struct.single_params - - preparer = self.preparer - supports_default_values = self.dialect.supports_default_values - - text = "INSERT " - - if insert_stmt._prefixes: - text += self._generate_prefixes( - insert_stmt, insert_stmt._prefixes, **kw - ) - - text += "INTO " - table_text = preparer.format_table(insert_stmt.table) - - if insert_stmt._hints: - _, table_text = self._setup_crud_hints(insert_stmt, table_text) - - if insert_stmt._independent_ctes: - self._dispatch_independent_ctes(insert_stmt, kw) - - text += table_text - - if crud_params_single or not supports_default_values: - text += " (%s)" % ", ".join( - [expr for _, expr, _, _ in crud_params_single] - ) - - # look for insertmanyvalues attributes that would have been configured - # by crud.py as it scanned through the columns to be part of the - # INSERT - use_insertmanyvalues = crud_params_struct.use_insertmanyvalues - named_sentinel_params: Optional[Sequence[str]] = None - add_sentinel_cols = None - implicit_sentinel = False - - returning_cols = self.implicit_returning or insert_stmt._returning - if returning_cols: - add_sentinel_cols = crud_params_struct.use_sentinel_columns - if add_sentinel_cols is not None: - assert use_insertmanyvalues - - # search for the sentinel column explicitly present - # in the INSERT columns list, and additionally check that - # this column has a bound parameter name set up that's in the - # parameter list. If both of these cases are present, it means - # we will have a client side value for the sentinel in each - # parameter set. - - _params_by_col = { - col: param_names - for col, _, _, param_names in crud_params_single - } - named_sentinel_params = [] - for _add_sentinel_col in add_sentinel_cols: - if _add_sentinel_col not in _params_by_col: - named_sentinel_params = None - break - param_name = self._within_exec_param_key_getter( - _add_sentinel_col - ) - if param_name not in _params_by_col[_add_sentinel_col]: - named_sentinel_params = None - break - named_sentinel_params.append(param_name) - - if named_sentinel_params is None: - # if we are not going to have a client side value for - # the sentinel in the parameter set, that means it's - # an autoincrement, an IDENTITY, or a server-side SQL - # expression like nextval('seqname'). So this is - # an "implicit" sentinel; we will look for it in - # RETURNING - # only, and then sort on it. For this case on PG, - # SQL Server we have to use a special INSERT form - # that guarantees the server side function lines up with - # the entries in the VALUES. - if ( - self.dialect.insertmanyvalues_implicit_sentinel - & InsertmanyvaluesSentinelOpts.ANY_AUTOINCREMENT - ): - implicit_sentinel = True - else: - # here, we are not using a sentinel at all - # and we are likely the SQLite dialect. - # The first add_sentinel_col that we have should not - # be marked as "insert_sentinel=True". if it was, - # an error should have been raised in - # _get_sentinel_column_for_table. - assert not add_sentinel_cols[0]._insert_sentinel, ( - "sentinel selection rules should have prevented " - "us from getting here for this dialect" - ) - - # always put the sentinel columns last. even if they are - # in the returning list already, they will be there twice - # then. - returning_cols = list(returning_cols) + list(add_sentinel_cols) - - returning_clause = self.returning_clause( - insert_stmt, - returning_cols, - populate_result_map=toplevel, - ) - - if self.returning_precedes_values: - text += " " + returning_clause - - else: - returning_clause = None - - if insert_stmt.select is not None: - # placed here by crud.py - select_text = self.process( - self.stack[-1]["insert_from_select"], insert_into=True, **kw - ) - - if self.ctes and self.dialect.cte_follows_insert: - nesting_level = len(self.stack) if not toplevel else None - text += " %s%s" % ( - self._render_cte_clause( - nesting_level=nesting_level, - include_following_stack=True, - ), - select_text, - ) - else: - text += " %s" % select_text - elif not crud_params_single and supports_default_values: - text += " DEFAULT VALUES" - if use_insertmanyvalues: - self._insertmanyvalues = _InsertManyValues( - True, - self.dialect.default_metavalue_token, - cast( - "List[crud._CrudParamElementStr]", crud_params_single - ), - counted_bindparam, - sort_by_parameter_order=( - insert_stmt._sort_by_parameter_order - ), - includes_upsert_behaviors=( - insert_stmt._post_values_clause is not None - ), - sentinel_columns=add_sentinel_cols, - num_sentinel_columns=( - len(add_sentinel_cols) if add_sentinel_cols else 0 - ), - implicit_sentinel=implicit_sentinel, - ) - elif compile_state._has_multi_parameters: - text += " VALUES %s" % ( - ", ".join( - "(%s)" - % (", ".join(value for _, _, value, _ in crud_param_set)) - for crud_param_set in crud_params_struct.all_multi_params - ), - ) - else: - insert_single_values_expr = ", ".join( - [ - value - for _, _, value, _ in cast( - "List[crud._CrudParamElementStr]", - crud_params_single, - ) - ] - ) - - if use_insertmanyvalues: - if ( - implicit_sentinel - and ( - self.dialect.insertmanyvalues_implicit_sentinel - & InsertmanyvaluesSentinelOpts.USE_INSERT_FROM_SELECT - ) - # this is checking if we have - # INSERT INTO table (id) VALUES (DEFAULT). - and not (crud_params_struct.is_default_metavalue_only) - ): - # if we have a sentinel column that is server generated, - # then for selected backends render the VALUES list as a - # subquery. This is the orderable form supported by - # PostgreSQL and SQL Server. - embed_sentinel_value = True - - render_bind_casts = ( - self.dialect.insertmanyvalues_implicit_sentinel - & InsertmanyvaluesSentinelOpts.RENDER_SELECT_COL_CASTS - ) - - colnames = ", ".join( - f"p{i}" for i, _ in enumerate(crud_params_single) - ) - - if render_bind_casts: - # render casts for the SELECT list. For PG, we are - # already rendering bind casts in the parameter list, - # selectively for the more "tricky" types like ARRAY. - # however, even for the "easy" types, if the parameter - # is NULL for every entry, PG gives up and says - # "it must be TEXT", which fails for other easy types - # like ints. So we cast on this side too. - colnames_w_cast = ", ".join( - self.render_bind_cast( - col.type, - col.type._unwrapped_dialect_impl(self.dialect), - f"p{i}", - ) - for i, (col, *_) in enumerate(crud_params_single) - ) - else: - colnames_w_cast = colnames - - text += ( - f" SELECT {colnames_w_cast} FROM " - f"(VALUES ({insert_single_values_expr})) " - f"AS imp_sen({colnames}, sen_counter) " - "ORDER BY sen_counter" - ) - else: - # otherwise, if no sentinel or backend doesn't support - # orderable subquery form, use a plain VALUES list - embed_sentinel_value = False - text += f" VALUES ({insert_single_values_expr})" - - self._insertmanyvalues = _InsertManyValues( - is_default_expr=False, - single_values_expr=insert_single_values_expr, - insert_crud_params=cast( - "List[crud._CrudParamElementStr]", - crud_params_single, - ), - num_positional_params_counted=counted_bindparam, - sort_by_parameter_order=( - insert_stmt._sort_by_parameter_order - ), - includes_upsert_behaviors=( - insert_stmt._post_values_clause is not None - ), - sentinel_columns=add_sentinel_cols, - num_sentinel_columns=( - len(add_sentinel_cols) if add_sentinel_cols else 0 - ), - sentinel_param_keys=named_sentinel_params, - implicit_sentinel=implicit_sentinel, - embed_values_counter=embed_sentinel_value, - ) - - else: - text += f" VALUES ({insert_single_values_expr})" - - if insert_stmt._post_values_clause is not None: - post_values_clause = self.process( - insert_stmt._post_values_clause, **kw - ) - if post_values_clause: - text += " " + post_values_clause - - if returning_clause and not self.returning_precedes_values: - text += " " + returning_clause - - if self.ctes and not self.dialect.cte_follows_insert: - nesting_level = len(self.stack) if not toplevel else None - text = ( - self._render_cte_clause( - nesting_level=nesting_level, - include_following_stack=True, - ) - + text - ) - - self.stack.pop(-1) - - return text - - def update_limit_clause(self, update_stmt): - """Provide a hook for MySQL to add LIMIT to the UPDATE""" - return None - - def update_tables_clause(self, update_stmt, from_table, extra_froms, **kw): - """Provide a hook to override the initial table clause - in an UPDATE statement. - - MySQL overrides this. - - """ - kw["asfrom"] = True - return from_table._compiler_dispatch(self, iscrud=True, **kw) - - def update_from_clause( - self, update_stmt, from_table, extra_froms, from_hints, **kw - ): - """Provide a hook to override the generation of an - UPDATE..FROM clause. - - MySQL and MSSQL override this. - - """ - raise NotImplementedError( - "This backend does not support multiple-table " - "criteria within UPDATE" - ) - - def visit_update(self, update_stmt, visiting_cte=None, **kw): - compile_state = update_stmt._compile_state_factory( - update_stmt, self, **kw - ) - update_stmt = compile_state.statement - - if visiting_cte is not None: - kw["visiting_cte"] = visiting_cte - toplevel = False - else: - toplevel = not self.stack - - if toplevel: - self.isupdate = True - if not self.dml_compile_state: - self.dml_compile_state = compile_state - if not self.compile_state: - self.compile_state = compile_state - - if self.linting & COLLECT_CARTESIAN_PRODUCTS: - from_linter = FromLinter({}, set()) - warn_linting = self.linting & WARN_LINTING - if toplevel: - self.from_linter = from_linter - else: - from_linter = None - warn_linting = False - - extra_froms = compile_state._extra_froms - is_multitable = bool(extra_froms) - - if is_multitable: - # main table might be a JOIN - main_froms = set(_from_objects(update_stmt.table)) - render_extra_froms = [ - f for f in extra_froms if f not in main_froms - ] - correlate_froms = main_froms.union(extra_froms) - else: - render_extra_froms = [] - correlate_froms = {update_stmt.table} - - self.stack.append( - { - "correlate_froms": correlate_froms, - "asfrom_froms": correlate_froms, - "selectable": update_stmt, - } - ) - - text = "UPDATE " - - if update_stmt._prefixes: - text += self._generate_prefixes( - update_stmt, update_stmt._prefixes, **kw - ) - - table_text = self.update_tables_clause( - update_stmt, - update_stmt.table, - render_extra_froms, - from_linter=from_linter, - **kw, - ) - crud_params_struct = crud._get_crud_params( - self, update_stmt, compile_state, toplevel, **kw - ) - crud_params = crud_params_struct.single_params - - if update_stmt._hints: - dialect_hints, table_text = self._setup_crud_hints( - update_stmt, table_text - ) - else: - dialect_hints = None - - if update_stmt._independent_ctes: - self._dispatch_independent_ctes(update_stmt, kw) - - text += table_text - - text += " SET " - text += ", ".join( - expr + "=" + value - for _, expr, value, _ in cast( - "List[Tuple[Any, str, str, Any]]", crud_params - ) - ) - - if self.implicit_returning or update_stmt._returning: - if self.returning_precedes_values: - text += " " + self.returning_clause( - update_stmt, - self.implicit_returning or update_stmt._returning, - populate_result_map=toplevel, - ) - - if extra_froms: - extra_from_text = self.update_from_clause( - update_stmt, - update_stmt.table, - render_extra_froms, - dialect_hints, - from_linter=from_linter, - **kw, - ) - if extra_from_text: - text += " " + extra_from_text - - if update_stmt._where_criteria: - t = self._generate_delimited_and_list( - update_stmt._where_criteria, from_linter=from_linter, **kw - ) - if t: - text += " WHERE " + t - - limit_clause = self.update_limit_clause(update_stmt) - if limit_clause: - text += " " + limit_clause - - if ( - self.implicit_returning or update_stmt._returning - ) and not self.returning_precedes_values: - text += " " + self.returning_clause( - update_stmt, - self.implicit_returning or update_stmt._returning, - populate_result_map=toplevel, - ) - - if self.ctes: - nesting_level = len(self.stack) if not toplevel else None - text = self._render_cte_clause(nesting_level=nesting_level) + text - - if warn_linting: - assert from_linter is not None - from_linter.warn(stmt_type="UPDATE") - - self.stack.pop(-1) - - return text - - def delete_extra_from_clause( - self, update_stmt, from_table, extra_froms, from_hints, **kw - ): - """Provide a hook to override the generation of an - DELETE..FROM clause. - - This can be used to implement DELETE..USING for example. - - MySQL and MSSQL override this. - - """ - raise NotImplementedError( - "This backend does not support multiple-table " - "criteria within DELETE" - ) - - def delete_table_clause(self, delete_stmt, from_table, extra_froms, **kw): - return from_table._compiler_dispatch( - self, asfrom=True, iscrud=True, **kw - ) - - def visit_delete(self, delete_stmt, visiting_cte=None, **kw): - compile_state = delete_stmt._compile_state_factory( - delete_stmt, self, **kw - ) - delete_stmt = compile_state.statement - - if visiting_cte is not None: - kw["visiting_cte"] = visiting_cte - toplevel = False - else: - toplevel = not self.stack - - if toplevel: - self.isdelete = True - if not self.dml_compile_state: - self.dml_compile_state = compile_state - if not self.compile_state: - self.compile_state = compile_state - - if self.linting & COLLECT_CARTESIAN_PRODUCTS: - from_linter = FromLinter({}, set()) - warn_linting = self.linting & WARN_LINTING - if toplevel: - self.from_linter = from_linter - else: - from_linter = None - warn_linting = False - - extra_froms = compile_state._extra_froms - - correlate_froms = {delete_stmt.table}.union(extra_froms) - self.stack.append( - { - "correlate_froms": correlate_froms, - "asfrom_froms": correlate_froms, - "selectable": delete_stmt, - } - ) - - text = "DELETE " - - if delete_stmt._prefixes: - text += self._generate_prefixes( - delete_stmt, delete_stmt._prefixes, **kw - ) - - text += "FROM " - - try: - table_text = self.delete_table_clause( - delete_stmt, - delete_stmt.table, - extra_froms, - from_linter=from_linter, - ) - except TypeError: - # anticipate 3rd party dialects that don't include **kw - # TODO: remove in 2.1 - table_text = self.delete_table_clause( - delete_stmt, delete_stmt.table, extra_froms - ) - if from_linter: - _ = self.process(delete_stmt.table, from_linter=from_linter) - - crud._get_crud_params(self, delete_stmt, compile_state, toplevel, **kw) - - if delete_stmt._hints: - dialect_hints, table_text = self._setup_crud_hints( - delete_stmt, table_text - ) - else: - dialect_hints = None - - if delete_stmt._independent_ctes: - self._dispatch_independent_ctes(delete_stmt, kw) - - text += table_text - - if ( - self.implicit_returning or delete_stmt._returning - ) and self.returning_precedes_values: - text += " " + self.returning_clause( - delete_stmt, - self.implicit_returning or delete_stmt._returning, - populate_result_map=toplevel, - ) - - if extra_froms: - extra_from_text = self.delete_extra_from_clause( - delete_stmt, - delete_stmt.table, - extra_froms, - dialect_hints, - from_linter=from_linter, - **kw, - ) - if extra_from_text: - text += " " + extra_from_text - - if delete_stmt._where_criteria: - t = self._generate_delimited_and_list( - delete_stmt._where_criteria, from_linter=from_linter, **kw - ) - if t: - text += " WHERE " + t - - if ( - self.implicit_returning or delete_stmt._returning - ) and not self.returning_precedes_values: - text += " " + self.returning_clause( - delete_stmt, - self.implicit_returning or delete_stmt._returning, - populate_result_map=toplevel, - ) - - if self.ctes: - nesting_level = len(self.stack) if not toplevel else None - text = self._render_cte_clause(nesting_level=nesting_level) + text - - if warn_linting: - assert from_linter is not None - from_linter.warn(stmt_type="DELETE") - - self.stack.pop(-1) - - return text - - def visit_savepoint(self, savepoint_stmt, **kw): - return "SAVEPOINT %s" % self.preparer.format_savepoint(savepoint_stmt) - - def visit_rollback_to_savepoint(self, savepoint_stmt, **kw): - return "ROLLBACK TO SAVEPOINT %s" % self.preparer.format_savepoint( - savepoint_stmt - ) - - def visit_release_savepoint(self, savepoint_stmt, **kw): - return "RELEASE SAVEPOINT %s" % self.preparer.format_savepoint( - savepoint_stmt - ) - - -class StrSQLCompiler(SQLCompiler): - """A :class:`.SQLCompiler` subclass which allows a small selection - of non-standard SQL features to render into a string value. - - The :class:`.StrSQLCompiler` is invoked whenever a Core expression - element is directly stringified without calling upon the - :meth:`_expression.ClauseElement.compile` method. - It can render a limited set - of non-standard SQL constructs to assist in basic stringification, - however for more substantial custom or dialect-specific SQL constructs, - it will be necessary to make use of - :meth:`_expression.ClauseElement.compile` - directly. - - .. seealso:: - - :ref:`faq_sql_expression_string` - - """ - - def _fallback_column_name(self, column): - return "<name unknown>" - - @util.preload_module("sqlalchemy.engine.url") - def visit_unsupported_compilation(self, element, err, **kw): - if element.stringify_dialect != "default": - url = util.preloaded.engine_url - dialect = url.URL.create(element.stringify_dialect).get_dialect()() - - compiler = dialect.statement_compiler( - dialect, None, _supporting_against=self - ) - if not isinstance(compiler, StrSQLCompiler): - return compiler.process(element, **kw) - - return super().visit_unsupported_compilation(element, err) - - def visit_getitem_binary(self, binary, operator, **kw): - return "%s[%s]" % ( - self.process(binary.left, **kw), - self.process(binary.right, **kw), - ) - - def visit_json_getitem_op_binary(self, binary, operator, **kw): - return self.visit_getitem_binary(binary, operator, **kw) - - def visit_json_path_getitem_op_binary(self, binary, operator, **kw): - return self.visit_getitem_binary(binary, operator, **kw) - - def visit_sequence(self, seq, **kw): - return "<next sequence value: %s>" % self.preparer.format_sequence(seq) - - def returning_clause( - self, - stmt: UpdateBase, - returning_cols: Sequence[ColumnElement[Any]], - *, - populate_result_map: bool, - **kw: Any, - ) -> str: - columns = [ - self._label_select_column(None, c, True, False, {}) - for c in base._select_iterables(returning_cols) - ] - return "RETURNING " + ", ".join(columns) - - def update_from_clause( - self, update_stmt, from_table, extra_froms, from_hints, **kw - ): - kw["asfrom"] = True - return "FROM " + ", ".join( - t._compiler_dispatch(self, fromhints=from_hints, **kw) - for t in extra_froms - ) - - def delete_extra_from_clause( - self, update_stmt, from_table, extra_froms, from_hints, **kw - ): - kw["asfrom"] = True - return ", " + ", ".join( - t._compiler_dispatch(self, fromhints=from_hints, **kw) - for t in extra_froms - ) - - def visit_empty_set_expr(self, type_, **kw): - return "SELECT 1 WHERE 1!=1" - - def get_from_hint_text(self, table, text): - return "[%s]" % text - - def visit_regexp_match_op_binary(self, binary, operator, **kw): - return self._generate_generic_binary(binary, " <regexp> ", **kw) - - def visit_not_regexp_match_op_binary(self, binary, operator, **kw): - return self._generate_generic_binary(binary, " <not regexp> ", **kw) - - def visit_regexp_replace_op_binary(self, binary, operator, **kw): - return "<regexp replace>(%s, %s)" % ( - binary.left._compiler_dispatch(self, **kw), - binary.right._compiler_dispatch(self, **kw), - ) - - def visit_try_cast(self, cast, **kwargs): - return "TRY_CAST(%s AS %s)" % ( - cast.clause._compiler_dispatch(self, **kwargs), - cast.typeclause._compiler_dispatch(self, **kwargs), - ) - - -class DDLCompiler(Compiled): - is_ddl = True - - if TYPE_CHECKING: - - def __init__( - self, - dialect: Dialect, - statement: ExecutableDDLElement, - schema_translate_map: Optional[SchemaTranslateMapType] = ..., - render_schema_translate: bool = ..., - compile_kwargs: Mapping[str, Any] = ..., - ): ... - - @util.memoized_property - def sql_compiler(self): - return self.dialect.statement_compiler( - self.dialect, None, schema_translate_map=self.schema_translate_map - ) - - @util.memoized_property - def type_compiler(self): - return self.dialect.type_compiler_instance - - def construct_params( - self, - params: Optional[_CoreSingleExecuteParams] = None, - extracted_parameters: Optional[Sequence[BindParameter[Any]]] = None, - escape_names: bool = True, - ) -> Optional[_MutableCoreSingleExecuteParams]: - return None - - def visit_ddl(self, ddl, **kwargs): - # table events can substitute table and schema name - context = ddl.context - if isinstance(ddl.target, schema.Table): - context = context.copy() - - preparer = self.preparer - path = preparer.format_table_seq(ddl.target) - if len(path) == 1: - table, sch = path[0], "" - else: - table, sch = path[-1], path[0] - - context.setdefault("table", table) - context.setdefault("schema", sch) - context.setdefault("fullname", preparer.format_table(ddl.target)) - - return self.sql_compiler.post_process_text(ddl.statement % context) - - def visit_create_schema(self, create, **kw): - text = "CREATE SCHEMA " - if create.if_not_exists: - text += "IF NOT EXISTS " - return text + self.preparer.format_schema(create.element) - - def visit_drop_schema(self, drop, **kw): - text = "DROP SCHEMA " - if drop.if_exists: - text += "IF EXISTS " - text += self.preparer.format_schema(drop.element) - if drop.cascade: - text += " CASCADE" - return text - - def visit_create_table(self, create, **kw): - table = create.element - preparer = self.preparer - - text = "\nCREATE " - if table._prefixes: - text += " ".join(table._prefixes) + " " - - text += "TABLE " - if create.if_not_exists: - text += "IF NOT EXISTS " - - text += preparer.format_table(table) + " " - - create_table_suffix = self.create_table_suffix(table) - if create_table_suffix: - text += create_table_suffix + " " - - text += "(" - - separator = "\n" - - # if only one primary key, specify it along with the column - first_pk = False - for create_column in create.columns: - column = create_column.element - try: - processed = self.process( - create_column, first_pk=column.primary_key and not first_pk - ) - if processed is not None: - text += separator - separator = ", \n" - text += "\t" + processed - if column.primary_key: - first_pk = True - except exc.CompileError as ce: - raise exc.CompileError( - "(in table '%s', column '%s'): %s" - % (table.description, column.name, ce.args[0]) - ) from ce - - const = self.create_table_constraints( - table, - _include_foreign_key_constraints=create.include_foreign_key_constraints, # noqa - ) - if const: - text += separator + "\t" + const - - text += "\n)%s\n\n" % self.post_create_table(table) - return text - - def visit_create_column(self, create, first_pk=False, **kw): - column = create.element - - if column.system: - return None - - text = self.get_column_specification(column, first_pk=first_pk) - const = " ".join( - self.process(constraint) for constraint in column.constraints - ) - if const: - text += " " + const - - return text - - def create_table_constraints( - self, table, _include_foreign_key_constraints=None, **kw - ): - # On some DB order is significant: visit PK first, then the - # other constraints (engine.ReflectionTest.testbasic failed on FB2) - constraints = [] - if table.primary_key: - constraints.append(table.primary_key) - - all_fkcs = table.foreign_key_constraints - if _include_foreign_key_constraints is not None: - omit_fkcs = all_fkcs.difference(_include_foreign_key_constraints) - else: - omit_fkcs = set() - - constraints.extend( - [ - c - for c in table._sorted_constraints - if c is not table.primary_key and c not in omit_fkcs - ] - ) - - return ", \n\t".join( - p - for p in ( - self.process(constraint) - for constraint in constraints - if (constraint._should_create_for_compiler(self)) - and ( - not self.dialect.supports_alter - or not getattr(constraint, "use_alter", False) - ) - ) - if p is not None - ) - - def visit_drop_table(self, drop, **kw): - text = "\nDROP TABLE " - if drop.if_exists: - text += "IF EXISTS " - return text + self.preparer.format_table(drop.element) - - def visit_drop_view(self, drop, **kw): - return "\nDROP VIEW " + self.preparer.format_table(drop.element) - - def _verify_index_table(self, index): - if index.table is None: - raise exc.CompileError( - "Index '%s' is not associated with any table." % index.name - ) - - def visit_create_index( - self, create, include_schema=False, include_table_schema=True, **kw - ): - index = create.element - self._verify_index_table(index) - preparer = self.preparer - text = "CREATE " - if index.unique: - text += "UNIQUE " - if index.name is None: - raise exc.CompileError( - "CREATE INDEX requires that the index have a name" - ) - - text += "INDEX " - if create.if_not_exists: - text += "IF NOT EXISTS " - - text += "%s ON %s (%s)" % ( - self._prepared_index_name(index, include_schema=include_schema), - preparer.format_table( - index.table, use_schema=include_table_schema - ), - ", ".join( - self.sql_compiler.process( - expr, include_table=False, literal_binds=True - ) - for expr in index.expressions - ), - ) - return text - - def visit_drop_index(self, drop, **kw): - index = drop.element - - if index.name is None: - raise exc.CompileError( - "DROP INDEX requires that the index have a name" - ) - text = "\nDROP INDEX " - if drop.if_exists: - text += "IF EXISTS " - - return text + self._prepared_index_name(index, include_schema=True) - - def _prepared_index_name(self, index, include_schema=False): - if index.table is not None: - effective_schema = self.preparer.schema_for_object(index.table) - else: - effective_schema = None - if include_schema and effective_schema: - schema_name = self.preparer.quote_schema(effective_schema) - else: - schema_name = None - - index_name = self.preparer.format_index(index) - - if schema_name: - index_name = schema_name + "." + index_name - return index_name - - def visit_add_constraint(self, create, **kw): - return "ALTER TABLE %s ADD %s" % ( - self.preparer.format_table(create.element.table), - self.process(create.element), - ) - - def visit_set_table_comment(self, create, **kw): - return "COMMENT ON TABLE %s IS %s" % ( - self.preparer.format_table(create.element), - self.sql_compiler.render_literal_value( - create.element.comment, sqltypes.String() - ), - ) - - def visit_drop_table_comment(self, drop, **kw): - return "COMMENT ON TABLE %s IS NULL" % self.preparer.format_table( - drop.element - ) - - def visit_set_column_comment(self, create, **kw): - return "COMMENT ON COLUMN %s IS %s" % ( - self.preparer.format_column( - create.element, use_table=True, use_schema=True - ), - self.sql_compiler.render_literal_value( - create.element.comment, sqltypes.String() - ), - ) - - def visit_drop_column_comment(self, drop, **kw): - return "COMMENT ON COLUMN %s IS NULL" % self.preparer.format_column( - drop.element, use_table=True - ) - - def visit_set_constraint_comment(self, create, **kw): - raise exc.UnsupportedCompilationError(self, type(create)) - - def visit_drop_constraint_comment(self, drop, **kw): - raise exc.UnsupportedCompilationError(self, type(drop)) - - def get_identity_options(self, identity_options): - text = [] - if identity_options.increment is not None: - text.append("INCREMENT BY %d" % identity_options.increment) - if identity_options.start is not None: - text.append("START WITH %d" % identity_options.start) - if identity_options.minvalue is not None: - text.append("MINVALUE %d" % identity_options.minvalue) - if identity_options.maxvalue is not None: - text.append("MAXVALUE %d" % identity_options.maxvalue) - if identity_options.nominvalue is not None: - text.append("NO MINVALUE") - if identity_options.nomaxvalue is not None: - text.append("NO MAXVALUE") - if identity_options.cache is not None: - text.append("CACHE %d" % identity_options.cache) - if identity_options.cycle is not None: - text.append("CYCLE" if identity_options.cycle else "NO CYCLE") - return " ".join(text) - - def visit_create_sequence(self, create, prefix=None, **kw): - text = "CREATE SEQUENCE " - if create.if_not_exists: - text += "IF NOT EXISTS " - text += self.preparer.format_sequence(create.element) - - if prefix: - text += prefix - options = self.get_identity_options(create.element) - if options: - text += " " + options - return text - - def visit_drop_sequence(self, drop, **kw): - text = "DROP SEQUENCE " - if drop.if_exists: - text += "IF EXISTS " - return text + self.preparer.format_sequence(drop.element) - - def visit_drop_constraint(self, drop, **kw): - constraint = drop.element - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - else: - formatted_name = None - - if formatted_name is None: - raise exc.CompileError( - "Can't emit DROP CONSTRAINT for constraint %r; " - "it has no name" % drop.element - ) - return "ALTER TABLE %s DROP CONSTRAINT %s%s%s" % ( - self.preparer.format_table(drop.element.table), - "IF EXISTS " if drop.if_exists else "", - formatted_name, - " CASCADE" if drop.cascade else "", - ) - - def get_column_specification(self, column, **kwargs): - colspec = ( - self.preparer.format_column(column) - + " " - + self.dialect.type_compiler_instance.process( - column.type, type_expression=column - ) - ) - default = self.get_column_default_string(column) - if default is not None: - colspec += " DEFAULT " + default - - if column.computed is not None: - colspec += " " + self.process(column.computed) - - if ( - column.identity is not None - and self.dialect.supports_identity_columns - ): - colspec += " " + self.process(column.identity) - - if not column.nullable and ( - not column.identity or not self.dialect.supports_identity_columns - ): - colspec += " NOT NULL" - return colspec - - def create_table_suffix(self, table): - return "" - - def post_create_table(self, table): - return "" - - def get_column_default_string(self, column): - if isinstance(column.server_default, schema.DefaultClause): - return self.render_default_string(column.server_default.arg) - else: - return None - - def render_default_string(self, default): - if isinstance(default, str): - return self.sql_compiler.render_literal_value( - default, sqltypes.STRINGTYPE - ) - else: - return self.sql_compiler.process(default, literal_binds=True) - - def visit_table_or_column_check_constraint(self, constraint, **kw): - if constraint.is_column_level: - return self.visit_column_check_constraint(constraint) - else: - return self.visit_check_constraint(constraint) - - def visit_check_constraint(self, constraint, **kw): - text = "" - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - if formatted_name is not None: - text += "CONSTRAINT %s " % formatted_name - text += "CHECK (%s)" % self.sql_compiler.process( - constraint.sqltext, include_table=False, literal_binds=True - ) - text += self.define_constraint_deferrability(constraint) - return text - - def visit_column_check_constraint(self, constraint, **kw): - text = "" - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - if formatted_name is not None: - text += "CONSTRAINT %s " % formatted_name - text += "CHECK (%s)" % self.sql_compiler.process( - constraint.sqltext, include_table=False, literal_binds=True - ) - text += self.define_constraint_deferrability(constraint) - return text - - def visit_primary_key_constraint(self, constraint, **kw): - if len(constraint) == 0: - return "" - text = "" - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - if formatted_name is not None: - text += "CONSTRAINT %s " % formatted_name - text += "PRIMARY KEY " - text += "(%s)" % ", ".join( - self.preparer.quote(c.name) - for c in ( - constraint.columns_autoinc_first - if constraint._implicit_generated - else constraint.columns - ) - ) - text += self.define_constraint_deferrability(constraint) - return text - - def visit_foreign_key_constraint(self, constraint, **kw): - preparer = self.preparer - text = "" - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - if formatted_name is not None: - text += "CONSTRAINT %s " % formatted_name - remote_table = list(constraint.elements)[0].column.table - text += "FOREIGN KEY(%s) REFERENCES %s (%s)" % ( - ", ".join( - preparer.quote(f.parent.name) for f in constraint.elements - ), - self.define_constraint_remote_table( - constraint, remote_table, preparer - ), - ", ".join( - preparer.quote(f.column.name) for f in constraint.elements - ), - ) - text += self.define_constraint_match(constraint) - text += self.define_constraint_cascades(constraint) - text += self.define_constraint_deferrability(constraint) - return text - - def define_constraint_remote_table(self, constraint, table, preparer): - """Format the remote table clause of a CREATE CONSTRAINT clause.""" - - return preparer.format_table(table) - - def visit_unique_constraint(self, constraint, **kw): - if len(constraint) == 0: - return "" - text = "" - if constraint.name is not None: - formatted_name = self.preparer.format_constraint(constraint) - if formatted_name is not None: - text += "CONSTRAINT %s " % formatted_name - text += "UNIQUE %s(%s)" % ( - self.define_unique_constraint_distinct(constraint, **kw), - ", ".join(self.preparer.quote(c.name) for c in constraint), - ) - text += self.define_constraint_deferrability(constraint) - return text - - def define_unique_constraint_distinct(self, constraint, **kw): - return "" - - def define_constraint_cascades(self, constraint): - text = "" - if constraint.ondelete is not None: - text += " ON DELETE %s" % self.preparer.validate_sql_phrase( - constraint.ondelete, FK_ON_DELETE - ) - if constraint.onupdate is not None: - text += " ON UPDATE %s" % self.preparer.validate_sql_phrase( - constraint.onupdate, FK_ON_UPDATE - ) - return text - - def define_constraint_deferrability(self, constraint): - text = "" - if constraint.deferrable is not None: - if constraint.deferrable: - text += " DEFERRABLE" - else: - text += " NOT DEFERRABLE" - if constraint.initially is not None: - text += " INITIALLY %s" % self.preparer.validate_sql_phrase( - constraint.initially, FK_INITIALLY - ) - return text - - def define_constraint_match(self, constraint): - text = "" - if constraint.match is not None: - text += " MATCH %s" % constraint.match - return text - - def visit_computed_column(self, generated, **kw): - text = "GENERATED ALWAYS AS (%s)" % self.sql_compiler.process( - generated.sqltext, include_table=False, literal_binds=True - ) - if generated.persisted is True: - text += " STORED" - elif generated.persisted is False: - text += " VIRTUAL" - return text - - def visit_identity_column(self, identity, **kw): - text = "GENERATED %s AS IDENTITY" % ( - "ALWAYS" if identity.always else "BY DEFAULT", - ) - options = self.get_identity_options(identity) - if options: - text += " (%s)" % options - return text - - -class GenericTypeCompiler(TypeCompiler): - def visit_FLOAT(self, type_, **kw): - return "FLOAT" - - def visit_DOUBLE(self, type_, **kw): - return "DOUBLE" - - def visit_DOUBLE_PRECISION(self, type_, **kw): - return "DOUBLE PRECISION" - - def visit_REAL(self, type_, **kw): - return "REAL" - - def visit_NUMERIC(self, type_, **kw): - if type_.precision is None: - return "NUMERIC" - elif type_.scale is None: - return "NUMERIC(%(precision)s)" % {"precision": type_.precision} - else: - return "NUMERIC(%(precision)s, %(scale)s)" % { - "precision": type_.precision, - "scale": type_.scale, - } - - def visit_DECIMAL(self, type_, **kw): - if type_.precision is None: - return "DECIMAL" - elif type_.scale is None: - return "DECIMAL(%(precision)s)" % {"precision": type_.precision} - else: - return "DECIMAL(%(precision)s, %(scale)s)" % { - "precision": type_.precision, - "scale": type_.scale, - } - - def visit_INTEGER(self, type_, **kw): - return "INTEGER" - - def visit_SMALLINT(self, type_, **kw): - return "SMALLINT" - - def visit_BIGINT(self, type_, **kw): - return "BIGINT" - - def visit_TIMESTAMP(self, type_, **kw): - return "TIMESTAMP" - - def visit_DATETIME(self, type_, **kw): - return "DATETIME" - - def visit_DATE(self, type_, **kw): - return "DATE" - - def visit_TIME(self, type_, **kw): - return "TIME" - - def visit_CLOB(self, type_, **kw): - return "CLOB" - - def visit_NCLOB(self, type_, **kw): - return "NCLOB" - - def _render_string_type(self, type_, name, length_override=None): - text = name - if length_override: - text += "(%d)" % length_override - elif type_.length: - text += "(%d)" % type_.length - if type_.collation: - text += ' COLLATE "%s"' % type_.collation - return text - - def visit_CHAR(self, type_, **kw): - return self._render_string_type(type_, "CHAR") - - def visit_NCHAR(self, type_, **kw): - return self._render_string_type(type_, "NCHAR") - - def visit_VARCHAR(self, type_, **kw): - return self._render_string_type(type_, "VARCHAR") - - def visit_NVARCHAR(self, type_, **kw): - return self._render_string_type(type_, "NVARCHAR") - - def visit_TEXT(self, type_, **kw): - return self._render_string_type(type_, "TEXT") - - def visit_UUID(self, type_, **kw): - return "UUID" - - def visit_BLOB(self, type_, **kw): - return "BLOB" - - def visit_BINARY(self, type_, **kw): - return "BINARY" + (type_.length and "(%d)" % type_.length or "") - - def visit_VARBINARY(self, type_, **kw): - return "VARBINARY" + (type_.length and "(%d)" % type_.length or "") - - def visit_BOOLEAN(self, type_, **kw): - return "BOOLEAN" - - def visit_uuid(self, type_, **kw): - if not type_.native_uuid or not self.dialect.supports_native_uuid: - return self._render_string_type(type_, "CHAR", length_override=32) - else: - return self.visit_UUID(type_, **kw) - - def visit_large_binary(self, type_, **kw): - return self.visit_BLOB(type_, **kw) - - def visit_boolean(self, type_, **kw): - return self.visit_BOOLEAN(type_, **kw) - - def visit_time(self, type_, **kw): - return self.visit_TIME(type_, **kw) - - def visit_datetime(self, type_, **kw): - return self.visit_DATETIME(type_, **kw) - - def visit_date(self, type_, **kw): - return self.visit_DATE(type_, **kw) - - def visit_big_integer(self, type_, **kw): - return self.visit_BIGINT(type_, **kw) - - def visit_small_integer(self, type_, **kw): - return self.visit_SMALLINT(type_, **kw) - - def visit_integer(self, type_, **kw): - return self.visit_INTEGER(type_, **kw) - - def visit_real(self, type_, **kw): - return self.visit_REAL(type_, **kw) - - def visit_float(self, type_, **kw): - return self.visit_FLOAT(type_, **kw) - - def visit_double(self, type_, **kw): - return self.visit_DOUBLE(type_, **kw) - - def visit_numeric(self, type_, **kw): - return self.visit_NUMERIC(type_, **kw) - - def visit_string(self, type_, **kw): - return self.visit_VARCHAR(type_, **kw) - - def visit_unicode(self, type_, **kw): - return self.visit_VARCHAR(type_, **kw) - - def visit_text(self, type_, **kw): - return self.visit_TEXT(type_, **kw) - - def visit_unicode_text(self, type_, **kw): - return self.visit_TEXT(type_, **kw) - - def visit_enum(self, type_, **kw): - return self.visit_VARCHAR(type_, **kw) - - def visit_null(self, type_, **kw): - raise exc.CompileError( - "Can't generate DDL for %r; " - "did you forget to specify a " - "type on this Column?" % type_ - ) - - def visit_type_decorator(self, type_, **kw): - return self.process(type_.type_engine(self.dialect), **kw) - - def visit_user_defined(self, type_, **kw): - return type_.get_col_spec(**kw) - - -class StrSQLTypeCompiler(GenericTypeCompiler): - def process(self, type_, **kw): - try: - _compiler_dispatch = type_._compiler_dispatch - except AttributeError: - return self._visit_unknown(type_, **kw) - else: - return _compiler_dispatch(self, **kw) - - def __getattr__(self, key): - if key.startswith("visit_"): - return self._visit_unknown - else: - raise AttributeError(key) - - def _visit_unknown(self, type_, **kw): - if type_.__class__.__name__ == type_.__class__.__name__.upper(): - return type_.__class__.__name__ - else: - return repr(type_) - - def visit_null(self, type_, **kw): - return "NULL" - - def visit_user_defined(self, type_, **kw): - try: - get_col_spec = type_.get_col_spec - except AttributeError: - return repr(type_) - else: - return get_col_spec(**kw) - - -class _SchemaForObjectCallable(Protocol): - def __call__(self, obj: Any) -> str: ... - - -class _BindNameForColProtocol(Protocol): - def __call__(self, col: ColumnClause[Any]) -> str: ... - - -class IdentifierPreparer: - """Handle quoting and case-folding of identifiers based on options.""" - - reserved_words = RESERVED_WORDS - - legal_characters = LEGAL_CHARACTERS - - illegal_initial_characters = ILLEGAL_INITIAL_CHARACTERS - - initial_quote: str - - final_quote: str - - _strings: MutableMapping[str, str] - - schema_for_object: _SchemaForObjectCallable = operator.attrgetter("schema") - """Return the .schema attribute for an object. - - For the default IdentifierPreparer, the schema for an object is always - the value of the ".schema" attribute. if the preparer is replaced - with one that has a non-empty schema_translate_map, the value of the - ".schema" attribute is rendered a symbol that will be converted to a - real schema name from the mapping post-compile. - - """ - - _includes_none_schema_translate: bool = False - - def __init__( - self, - dialect, - initial_quote='"', - final_quote=None, - escape_quote='"', - quote_case_sensitive_collations=True, - omit_schema=False, - ): - """Construct a new ``IdentifierPreparer`` object. - - initial_quote - Character that begins a delimited identifier. - - final_quote - Character that ends a delimited identifier. Defaults to - `initial_quote`. - - omit_schema - Prevent prepending schema name. Useful for databases that do - not support schemae. - """ - - self.dialect = dialect - self.initial_quote = initial_quote - self.final_quote = final_quote or self.initial_quote - self.escape_quote = escape_quote - self.escape_to_quote = self.escape_quote * 2 - self.omit_schema = omit_schema - self.quote_case_sensitive_collations = quote_case_sensitive_collations - self._strings = {} - self._double_percents = self.dialect.paramstyle in ( - "format", - "pyformat", - ) - - def _with_schema_translate(self, schema_translate_map): - prep = self.__class__.__new__(self.__class__) - prep.__dict__.update(self.__dict__) - - includes_none = None in schema_translate_map - - def symbol_getter(obj): - name = obj.schema - if obj._use_schema_map and (name is not None or includes_none): - if name is not None and ("[" in name or "]" in name): - raise exc.CompileError( - "Square bracket characters ([]) not supported " - "in schema translate name '%s'" % name - ) - return quoted_name( - "__[SCHEMA_%s]" % (name or "_none"), quote=False - ) - else: - return obj.schema - - prep.schema_for_object = symbol_getter - prep._includes_none_schema_translate = includes_none - return prep - - def _render_schema_translates(self, statement, schema_translate_map): - d = schema_translate_map - if None in d: - if not self._includes_none_schema_translate: - raise exc.InvalidRequestError( - "schema translate map which previously did not have " - "`None` present as a key now has `None` present; compiled " - "statement may lack adequate placeholders. Please use " - "consistent keys in successive " - "schema_translate_map dictionaries." - ) - - d["_none"] = d[None] - - def replace(m): - name = m.group(2) - if name in d: - effective_schema = d[name] - else: - if name in (None, "_none"): - raise exc.InvalidRequestError( - "schema translate map which previously had `None` " - "present as a key now no longer has it present; don't " - "know how to apply schema for compiled statement. " - "Please use consistent keys in successive " - "schema_translate_map dictionaries." - ) - effective_schema = name - - if not effective_schema: - effective_schema = self.dialect.default_schema_name - if not effective_schema: - # TODO: no coverage here - raise exc.CompileError( - "Dialect has no default schema name; can't " - "use None as dynamic schema target." - ) - return self.quote_schema(effective_schema) - - return re.sub(r"(__\[SCHEMA_([^\]]+)\])", replace, statement) - - def _escape_identifier(self, value: str) -> str: - """Escape an identifier. - - Subclasses should override this to provide database-dependent - escaping behavior. - """ - - value = value.replace(self.escape_quote, self.escape_to_quote) - if self._double_percents: - value = value.replace("%", "%%") - return value - - def _unescape_identifier(self, value: str) -> str: - """Canonicalize an escaped identifier. - - Subclasses should override this to provide database-dependent - unescaping behavior that reverses _escape_identifier. - """ - - return value.replace(self.escape_to_quote, self.escape_quote) - - def validate_sql_phrase(self, element, reg): - """keyword sequence filter. - - a filter for elements that are intended to represent keyword sequences, - such as "INITIALLY", "INITIALLY DEFERRED", etc. no special characters - should be present. - - .. versionadded:: 1.3 - - """ - - if element is not None and not reg.match(element): - raise exc.CompileError( - "Unexpected SQL phrase: %r (matching against %r)" - % (element, reg.pattern) - ) - return element - - def quote_identifier(self, value: str) -> str: - """Quote an identifier. - - Subclasses should override this to provide database-dependent - quoting behavior. - """ - - return ( - self.initial_quote - + self._escape_identifier(value) - + self.final_quote - ) - - def _requires_quotes(self, value: str) -> bool: - """Return True if the given identifier requires quoting.""" - lc_value = value.lower() - return ( - lc_value in self.reserved_words - or value[0] in self.illegal_initial_characters - or not self.legal_characters.match(str(value)) - or (lc_value != value) - ) - - def _requires_quotes_illegal_chars(self, value): - """Return True if the given identifier requires quoting, but - not taking case convention into account.""" - return not self.legal_characters.match(str(value)) - - def quote_schema(self, schema: str, force: Any = None) -> str: - """Conditionally quote a schema name. - - - The name is quoted if it is a reserved word, contains quote-necessary - characters, or is an instance of :class:`.quoted_name` which includes - ``quote`` set to ``True``. - - Subclasses can override this to provide database-dependent - quoting behavior for schema names. - - :param schema: string schema name - :param force: unused - - .. deprecated:: 0.9 - - The :paramref:`.IdentifierPreparer.quote_schema.force` - parameter is deprecated and will be removed in a future - release. This flag has no effect on the behavior of the - :meth:`.IdentifierPreparer.quote` method; please refer to - :class:`.quoted_name`. - - """ - if force is not None: - # not using the util.deprecated_params() decorator in this - # case because of the additional function call overhead on this - # very performance-critical spot. - util.warn_deprecated( - "The IdentifierPreparer.quote_schema.force parameter is " - "deprecated and will be removed in a future release. This " - "flag has no effect on the behavior of the " - "IdentifierPreparer.quote method; please refer to " - "quoted_name().", - # deprecated 0.9. warning from 1.3 - version="0.9", - ) - - return self.quote(schema) - - def quote(self, ident: str, force: Any = None) -> str: - """Conditionally quote an identifier. - - The identifier is quoted if it is a reserved word, contains - quote-necessary characters, or is an instance of - :class:`.quoted_name` which includes ``quote`` set to ``True``. - - Subclasses can override this to provide database-dependent - quoting behavior for identifier names. - - :param ident: string identifier - :param force: unused - - .. deprecated:: 0.9 - - The :paramref:`.IdentifierPreparer.quote.force` - parameter is deprecated and will be removed in a future - release. This flag has no effect on the behavior of the - :meth:`.IdentifierPreparer.quote` method; please refer to - :class:`.quoted_name`. - - """ - if force is not None: - # not using the util.deprecated_params() decorator in this - # case because of the additional function call overhead on this - # very performance-critical spot. - util.warn_deprecated( - "The IdentifierPreparer.quote.force parameter is " - "deprecated and will be removed in a future release. This " - "flag has no effect on the behavior of the " - "IdentifierPreparer.quote method; please refer to " - "quoted_name().", - # deprecated 0.9. warning from 1.3 - version="0.9", - ) - - force = getattr(ident, "quote", None) - - if force is None: - if ident in self._strings: - return self._strings[ident] - else: - if self._requires_quotes(ident): - self._strings[ident] = self.quote_identifier(ident) - else: - self._strings[ident] = ident - return self._strings[ident] - elif force: - return self.quote_identifier(ident) - else: - return ident - - def format_collation(self, collation_name): - if self.quote_case_sensitive_collations: - return self.quote(collation_name) - else: - return collation_name - - def format_sequence(self, sequence, use_schema=True): - name = self.quote(sequence.name) - - effective_schema = self.schema_for_object(sequence) - - if ( - not self.omit_schema - and use_schema - and effective_schema is not None - ): - name = self.quote_schema(effective_schema) + "." + name - return name - - def format_label( - self, label: Label[Any], name: Optional[str] = None - ) -> str: - return self.quote(name or label.name) - - def format_alias( - self, alias: Optional[AliasedReturnsRows], name: Optional[str] = None - ) -> str: - if name is None: - assert alias is not None - return self.quote(alias.name) - else: - return self.quote(name) - - def format_savepoint(self, savepoint, name=None): - # Running the savepoint name through quoting is unnecessary - # for all known dialects. This is here to support potential - # third party use cases - ident = name or savepoint.ident - if self._requires_quotes(ident): - ident = self.quote_identifier(ident) - return ident - - @util.preload_module("sqlalchemy.sql.naming") - def format_constraint(self, constraint, _alembic_quote=True): - naming = util.preloaded.sql_naming - - if constraint.name is _NONE_NAME: - name = naming._constraint_name_for_table( - constraint, constraint.table - ) - - if name is None: - return None - else: - name = constraint.name - - if constraint.__visit_name__ == "index": - return self.truncate_and_render_index_name( - name, _alembic_quote=_alembic_quote - ) - else: - return self.truncate_and_render_constraint_name( - name, _alembic_quote=_alembic_quote - ) - - def truncate_and_render_index_name(self, name, _alembic_quote=True): - # calculate these at format time so that ad-hoc changes - # to dialect.max_identifier_length etc. can be reflected - # as IdentifierPreparer is long lived - max_ = ( - self.dialect.max_index_name_length - or self.dialect.max_identifier_length - ) - return self._truncate_and_render_maxlen_name( - name, max_, _alembic_quote - ) - - def truncate_and_render_constraint_name(self, name, _alembic_quote=True): - # calculate these at format time so that ad-hoc changes - # to dialect.max_identifier_length etc. can be reflected - # as IdentifierPreparer is long lived - max_ = ( - self.dialect.max_constraint_name_length - or self.dialect.max_identifier_length - ) - return self._truncate_and_render_maxlen_name( - name, max_, _alembic_quote - ) - - def _truncate_and_render_maxlen_name(self, name, max_, _alembic_quote): - if isinstance(name, elements._truncated_label): - if len(name) > max_: - name = name[0 : max_ - 8] + "_" + util.md5_hex(name)[-4:] - else: - self.dialect.validate_identifier(name) - - if not _alembic_quote: - return name - else: - return self.quote(name) - - def format_index(self, index): - return self.format_constraint(index) - - def format_table(self, table, use_schema=True, name=None): - """Prepare a quoted table and schema name.""" - - if name is None: - name = table.name - - result = self.quote(name) - - effective_schema = self.schema_for_object(table) - - if not self.omit_schema and use_schema and effective_schema: - result = self.quote_schema(effective_schema) + "." + result - return result - - def format_schema(self, name): - """Prepare a quoted schema name.""" - - return self.quote(name) - - def format_label_name( - self, - name, - anon_map=None, - ): - """Prepare a quoted column name.""" - - if anon_map is not None and isinstance( - name, elements._truncated_label - ): - name = name.apply_map(anon_map) - - return self.quote(name) - - def format_column( - self, - column, - use_table=False, - name=None, - table_name=None, - use_schema=False, - anon_map=None, - ): - """Prepare a quoted column name.""" - - if name is None: - name = column.name - - if anon_map is not None and isinstance( - name, elements._truncated_label - ): - name = name.apply_map(anon_map) - - if not getattr(column, "is_literal", False): - if use_table: - return ( - self.format_table( - column.table, use_schema=use_schema, name=table_name - ) - + "." - + self.quote(name) - ) - else: - return self.quote(name) - else: - # literal textual elements get stuck into ColumnClause a lot, - # which shouldn't get quoted - - if use_table: - return ( - self.format_table( - column.table, use_schema=use_schema, name=table_name - ) - + "." - + name - ) - else: - return name - - def format_table_seq(self, table, use_schema=True): - """Format table name and schema as a tuple.""" - - # Dialects with more levels in their fully qualified references - # ('database', 'owner', etc.) could override this and return - # a longer sequence. - - effective_schema = self.schema_for_object(table) - - if not self.omit_schema and use_schema and effective_schema: - return ( - self.quote_schema(effective_schema), - self.format_table(table, use_schema=False), - ) - else: - return (self.format_table(table, use_schema=False),) - - @util.memoized_property - def _r_identifiers(self): - initial, final, escaped_final = ( - re.escape(s) - for s in ( - self.initial_quote, - self.final_quote, - self._escape_identifier(self.final_quote), - ) - ) - r = re.compile( - r"(?:" - r"(?:%(initial)s((?:%(escaped)s|[^%(final)s])+)%(final)s" - r"|([^\.]+))(?=\.|$))+" - % {"initial": initial, "final": final, "escaped": escaped_final} - ) - return r - - def unformat_identifiers(self, identifiers): - """Unpack 'schema.table.column'-like strings into components.""" - - r = self._r_identifiers - return [ - self._unescape_identifier(i) - for i in [a or b for a, b in r.findall(identifiers)] - ] diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/crud.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/crud.py deleted file mode 100644 index 499a19d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/crud.py +++ /dev/null @@ -1,1669 +0,0 @@ -# sql/crud.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""Functions used by compiler.py to determine the parameters rendered -within INSERT and UPDATE statements. - -""" -from __future__ import annotations - -import functools -import operator -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Iterable -from typing import List -from typing import MutableMapping -from typing import NamedTuple -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Set -from typing import Tuple -from typing import TYPE_CHECKING -from typing import Union - -from . import coercions -from . import dml -from . import elements -from . import roles -from .base import _DefaultDescriptionTuple -from .dml import isinsert as _compile_state_isinsert -from .elements import ColumnClause -from .schema import default_is_clause_element -from .schema import default_is_sequence -from .selectable import Select -from .selectable import TableClause -from .. import exc -from .. import util -from ..util.typing import Literal - -if TYPE_CHECKING: - from .compiler import _BindNameForColProtocol - from .compiler import SQLCompiler - from .dml import _DMLColumnElement - from .dml import DMLState - from .dml import ValuesBase - from .elements import ColumnElement - from .elements import KeyedColumnElement - from .schema import _SQLExprDefault - from .schema import Column - -REQUIRED = util.symbol( - "REQUIRED", - """ -Placeholder for the value within a :class:`.BindParameter` -which is required to be present when the statement is passed -to :meth:`_engine.Connection.execute`. - -This symbol is typically used when a :func:`_expression.insert` -or :func:`_expression.update` statement is compiled without parameter -values present. - -""", -) - - -def _as_dml_column(c: ColumnElement[Any]) -> ColumnClause[Any]: - if not isinstance(c, ColumnClause): - raise exc.CompileError( - f"Can't create DML statement against column expression {c!r}" - ) - return c - - -_CrudParamElement = Tuple[ - "ColumnElement[Any]", - str, # column name - Optional[ - Union[str, "_SQLExprDefault"] - ], # bound parameter string or SQL expression to apply - Iterable[str], -] -_CrudParamElementStr = Tuple[ - "KeyedColumnElement[Any]", - str, # column name - str, # bound parameter string - Iterable[str], -] -_CrudParamElementSQLExpr = Tuple[ - "ColumnClause[Any]", - str, - "_SQLExprDefault", # SQL expression to apply - Iterable[str], -] - -_CrudParamSequence = List[_CrudParamElement] - - -class _CrudParams(NamedTuple): - single_params: _CrudParamSequence - all_multi_params: List[Sequence[_CrudParamElementStr]] - is_default_metavalue_only: bool = False - use_insertmanyvalues: bool = False - use_sentinel_columns: Optional[Sequence[Column[Any]]] = None - - -def _get_crud_params( - compiler: SQLCompiler, - stmt: ValuesBase, - compile_state: DMLState, - toplevel: bool, - **kw: Any, -) -> _CrudParams: - """create a set of tuples representing column/string pairs for use - in an INSERT or UPDATE statement. - - Also generates the Compiled object's postfetch, prefetch, and - returning column collections, used for default handling and ultimately - populating the CursorResult's prefetch_cols() and postfetch_cols() - collections. - - """ - - # note: the _get_crud_params() system was written with the notion in mind - # that INSERT, UPDATE, DELETE are always the top level statement and - # that there is only one of them. With the addition of CTEs that can - # make use of DML, this assumption is no longer accurate; the DML - # statement is not necessarily the top-level "row returning" thing - # and it is also theoretically possible (fortunately nobody has asked yet) - # to have a single statement with multiple DMLs inside of it via CTEs. - - # the current _get_crud_params() design doesn't accommodate these cases - # right now. It "just works" for a CTE that has a single DML inside of - # it, and for a CTE with multiple DML, it's not clear what would happen. - - # overall, the "compiler.XYZ" collections here would need to be in a - # per-DML structure of some kind, and DefaultDialect would need to - # navigate these collections on a per-statement basis, with additional - # emphasis on the "toplevel returning data" statement. However we - # still need to run through _get_crud_params() for all DML as we have - # Python / SQL generated column defaults that need to be rendered. - - # if there is user need for this kind of thing, it's likely a post 2.0 - # kind of change as it would require deep changes to DefaultDialect - # as well as here. - - compiler.postfetch = [] - compiler.insert_prefetch = [] - compiler.update_prefetch = [] - compiler.implicit_returning = [] - - visiting_cte = kw.get("visiting_cte", None) - if visiting_cte is not None: - # for insert -> CTE -> insert, don't populate an incoming - # _crud_accumulate_bind_names collection; the INSERT we process here - # will not be inline within the VALUES of the enclosing INSERT as the - # CTE is placed on the outside. See issue #9173 - kw.pop("accumulate_bind_names", None) - assert ( - "accumulate_bind_names" not in kw - ), "Don't know how to handle insert within insert without a CTE" - - # getters - these are normally just column.key, - # but in the case of mysql multi-table update, the rules for - # .key must conditionally take tablename into account - ( - _column_as_key, - _getattr_col_key, - _col_bind_name, - ) = _key_getters_for_crud_column(compiler, stmt, compile_state) - - compiler._get_bind_name_for_col = _col_bind_name - - if stmt._returning and stmt._return_defaults: - raise exc.CompileError( - "Can't compile statement that includes returning() and " - "return_defaults() simultaneously" - ) - - if compile_state.isdelete: - _setup_delete_return_defaults( - compiler, - stmt, - compile_state, - (), - _getattr_col_key, - _column_as_key, - _col_bind_name, - (), - (), - toplevel, - kw, - ) - return _CrudParams([], []) - - # no parameters in the statement, no parameters in the - # compiled params - return binds for all columns - if compiler.column_keys is None and compile_state._no_parameters: - return _CrudParams( - [ - ( - c, - compiler.preparer.format_column(c), - _create_bind_param(compiler, c, None, required=True), - (c.key,), - ) - for c in stmt.table.columns - if not c._omit_from_statements - ], - [], - ) - - stmt_parameter_tuples: Optional[ - List[Tuple[Union[str, ColumnClause[Any]], Any]] - ] - spd: Optional[MutableMapping[_DMLColumnElement, Any]] - - if ( - _compile_state_isinsert(compile_state) - and compile_state._has_multi_parameters - ): - mp = compile_state._multi_parameters - assert mp is not None - spd = mp[0] - stmt_parameter_tuples = list(spd.items()) - spd_str_key = {_column_as_key(key) for key in spd} - elif compile_state._ordered_values: - spd = compile_state._dict_parameters - stmt_parameter_tuples = compile_state._ordered_values - assert spd is not None - spd_str_key = {_column_as_key(key) for key in spd} - elif compile_state._dict_parameters: - spd = compile_state._dict_parameters - stmt_parameter_tuples = list(spd.items()) - spd_str_key = {_column_as_key(key) for key in spd} - else: - stmt_parameter_tuples = spd = spd_str_key = None - - # if we have statement parameters - set defaults in the - # compiled params - if compiler.column_keys is None: - parameters = {} - elif stmt_parameter_tuples: - assert spd_str_key is not None - parameters = { - _column_as_key(key): REQUIRED - for key in compiler.column_keys - if key not in spd_str_key - } - else: - parameters = { - _column_as_key(key): REQUIRED for key in compiler.column_keys - } - - # create a list of column assignment clauses as tuples - values: List[_CrudParamElement] = [] - - if stmt_parameter_tuples is not None: - _get_stmt_parameter_tuples_params( - compiler, - compile_state, - parameters, - stmt_parameter_tuples, - _column_as_key, - values, - kw, - ) - - check_columns: Dict[str, ColumnClause[Any]] = {} - - # special logic that only occurs for multi-table UPDATE - # statements - if dml.isupdate(compile_state) and compile_state.is_multitable: - _get_update_multitable_params( - compiler, - stmt, - compile_state, - stmt_parameter_tuples, - check_columns, - _col_bind_name, - _getattr_col_key, - values, - kw, - ) - - if _compile_state_isinsert(compile_state) and stmt._select_names: - # is an insert from select, is not a multiparams - - assert not compile_state._has_multi_parameters - - _scan_insert_from_select_cols( - compiler, - stmt, - compile_state, - parameters, - _getattr_col_key, - _column_as_key, - _col_bind_name, - check_columns, - values, - toplevel, - kw, - ) - use_insertmanyvalues = False - use_sentinel_columns = None - else: - use_insertmanyvalues, use_sentinel_columns = _scan_cols( - compiler, - stmt, - compile_state, - parameters, - _getattr_col_key, - _column_as_key, - _col_bind_name, - check_columns, - values, - toplevel, - kw, - ) - - if parameters and stmt_parameter_tuples: - check = ( - set(parameters) - .intersection(_column_as_key(k) for k, v in stmt_parameter_tuples) - .difference(check_columns) - ) - if check: - raise exc.CompileError( - "Unconsumed column names: %s" - % (", ".join("%s" % (c,) for c in check)) - ) - - is_default_metavalue_only = False - - if ( - _compile_state_isinsert(compile_state) - and compile_state._has_multi_parameters - ): - # is a multiparams, is not an insert from a select - assert not stmt._select_names - multi_extended_values = _extend_values_for_multiparams( - compiler, - stmt, - compile_state, - cast( - "Sequence[_CrudParamElementStr]", - values, - ), - cast("Callable[..., str]", _column_as_key), - kw, - ) - return _CrudParams(values, multi_extended_values) - elif ( - not values - and compiler.for_executemany - and compiler.dialect.supports_default_metavalue - ): - # convert an "INSERT DEFAULT VALUES" - # into INSERT (firstcol) VALUES (DEFAULT) which can be turned - # into an in-place multi values. This supports - # insert_executemany_returning mode :) - values = [ - ( - _as_dml_column(stmt.table.columns[0]), - compiler.preparer.format_column(stmt.table.columns[0]), - compiler.dialect.default_metavalue_token, - (), - ) - ] - is_default_metavalue_only = True - - return _CrudParams( - values, - [], - is_default_metavalue_only=is_default_metavalue_only, - use_insertmanyvalues=use_insertmanyvalues, - use_sentinel_columns=use_sentinel_columns, - ) - - -@overload -def _create_bind_param( - compiler: SQLCompiler, - col: ColumnElement[Any], - value: Any, - process: Literal[True] = ..., - required: bool = False, - name: Optional[str] = None, - **kw: Any, -) -> str: ... - - -@overload -def _create_bind_param( - compiler: SQLCompiler, - col: ColumnElement[Any], - value: Any, - **kw: Any, -) -> str: ... - - -def _create_bind_param( - compiler: SQLCompiler, - col: ColumnElement[Any], - value: Any, - process: bool = True, - required: bool = False, - name: Optional[str] = None, - **kw: Any, -) -> Union[str, elements.BindParameter[Any]]: - if name is None: - name = col.key - bindparam = elements.BindParameter( - name, value, type_=col.type, required=required - ) - bindparam._is_crud = True - if process: - return bindparam._compiler_dispatch(compiler, **kw) - else: - return bindparam - - -def _handle_values_anonymous_param(compiler, col, value, name, **kw): - # the insert() and update() constructs as of 1.4 will now produce anonymous - # bindparam() objects in the values() collections up front when given plain - # literal values. This is so that cache key behaviors, which need to - # produce bound parameters in deterministic order without invoking any - # compilation here, can be applied to these constructs when they include - # values() (but not yet multi-values, which are not included in caching - # right now). - # - # in order to produce the desired "crud" style name for these parameters, - # which will also be targetable in engine/default.py through the usual - # conventions, apply our desired name to these unique parameters by - # populating the compiler truncated names cache with the desired name, - # rather than having - # compiler.visit_bindparam()->compiler._truncated_identifier make up a - # name. Saves on call counts also. - - # for INSERT/UPDATE that's a CTE, we don't need names to match to - # external parameters and these would also conflict in the case where - # multiple insert/update are combined together using CTEs - is_cte = "visiting_cte" in kw - - if ( - not is_cte - and value.unique - and isinstance(value.key, elements._truncated_label) - ): - compiler.truncated_names[("bindparam", value.key)] = name - - if value.type._isnull: - # either unique parameter, or other bound parameters that were - # passed in directly - # set type to that of the column unconditionally - value = value._with_binary_element_type(col.type) - - return value._compiler_dispatch(compiler, **kw) - - -def _key_getters_for_crud_column( - compiler: SQLCompiler, stmt: ValuesBase, compile_state: DMLState -) -> Tuple[ - Callable[[Union[str, ColumnClause[Any]]], Union[str, Tuple[str, str]]], - Callable[[ColumnClause[Any]], Union[str, Tuple[str, str]]], - _BindNameForColProtocol, -]: - if dml.isupdate(compile_state) and compile_state._extra_froms: - # when extra tables are present, refer to the columns - # in those extra tables as table-qualified, including in - # dictionaries and when rendering bind param names. - # the "main" table of the statement remains unqualified, - # allowing the most compatibility with a non-multi-table - # statement. - _et = set(compile_state._extra_froms) - - c_key_role = functools.partial( - coercions.expect_as_key, roles.DMLColumnRole - ) - - def _column_as_key( - key: Union[ColumnClause[Any], str] - ) -> Union[str, Tuple[str, str]]: - str_key = c_key_role(key) - if hasattr(key, "table") and key.table in _et: - return (key.table.name, str_key) # type: ignore - else: - return str_key - - def _getattr_col_key( - col: ColumnClause[Any], - ) -> Union[str, Tuple[str, str]]: - if col.table in _et: - return (col.table.name, col.key) # type: ignore - else: - return col.key - - def _col_bind_name(col: ColumnClause[Any]) -> str: - if col.table in _et: - if TYPE_CHECKING: - assert isinstance(col.table, TableClause) - return "%s_%s" % (col.table.name, col.key) - else: - return col.key - - else: - _column_as_key = functools.partial( - coercions.expect_as_key, roles.DMLColumnRole - ) - _getattr_col_key = _col_bind_name = operator.attrgetter("key") # type: ignore # noqa: E501 - - return _column_as_key, _getattr_col_key, _col_bind_name - - -def _scan_insert_from_select_cols( - compiler, - stmt, - compile_state, - parameters, - _getattr_col_key, - _column_as_key, - _col_bind_name, - check_columns, - values, - toplevel, - kw, -): - cols = [stmt.table.c[_column_as_key(name)] for name in stmt._select_names] - - assert compiler.stack[-1]["selectable"] is stmt - - compiler.stack[-1]["insert_from_select"] = stmt.select - - add_select_cols: List[_CrudParamElementSQLExpr] = [] - if stmt.include_insert_from_select_defaults: - col_set = set(cols) - for col in stmt.table.columns: - # omit columns that were not in the SELECT statement. - # this will omit columns marked as omit_from_statements naturally, - # as long as that col was not explicit in the SELECT. - # if an omit_from_statements col has a "default" on it, then - # we need to include it, as these defaults should still fire off. - # but, if it has that default and it's the "sentinel" default, - # we don't do sentinel default operations for insert_from_select - # here so we again omit it. - if ( - col not in col_set - and col.default - and not col.default.is_sentinel - ): - cols.append(col) - - for c in cols: - col_key = _getattr_col_key(c) - if col_key in parameters and col_key not in check_columns: - parameters.pop(col_key) - values.append((c, compiler.preparer.format_column(c), None, ())) - else: - _append_param_insert_select_hasdefault( - compiler, stmt, c, add_select_cols, kw - ) - - if add_select_cols: - values.extend(add_select_cols) - ins_from_select = compiler.stack[-1]["insert_from_select"] - if not isinstance(ins_from_select, Select): - raise exc.CompileError( - f"Can't extend statement for INSERT..FROM SELECT to include " - f"additional default-holding column(s) " - f"""{ - ', '.join(repr(key) for _, key, _, _ in add_select_cols) - }. Convert the selectable to a subquery() first, or pass """ - "include_defaults=False to Insert.from_select() to skip these " - "columns." - ) - ins_from_select = ins_from_select._generate() - # copy raw_columns - ins_from_select._raw_columns = list(ins_from_select._raw_columns) + [ - expr for _, _, expr, _ in add_select_cols - ] - compiler.stack[-1]["insert_from_select"] = ins_from_select - - -def _scan_cols( - compiler, - stmt, - compile_state, - parameters, - _getattr_col_key, - _column_as_key, - _col_bind_name, - check_columns, - values, - toplevel, - kw, -): - ( - need_pks, - implicit_returning, - implicit_return_defaults, - postfetch_lastrowid, - use_insertmanyvalues, - use_sentinel_columns, - ) = _get_returning_modifiers(compiler, stmt, compile_state, toplevel) - - assert compile_state.isupdate or compile_state.isinsert - - if compile_state._parameter_ordering: - parameter_ordering = [ - _column_as_key(key) for key in compile_state._parameter_ordering - ] - ordered_keys = set(parameter_ordering) - cols = [ - stmt.table.c[key] - for key in parameter_ordering - if isinstance(key, str) and key in stmt.table.c - ] + [c for c in stmt.table.c if c.key not in ordered_keys] - - else: - cols = stmt.table.columns - - isinsert = _compile_state_isinsert(compile_state) - if isinsert and not compile_state._has_multi_parameters: - # new rules for #7998. fetch lastrowid or implicit returning - # for autoincrement column even if parameter is NULL, for DBs that - # override NULL param for primary key (sqlite, mysql/mariadb) - autoincrement_col = stmt.table._autoincrement_column - insert_null_pk_still_autoincrements = ( - compiler.dialect.insert_null_pk_still_autoincrements - ) - else: - autoincrement_col = insert_null_pk_still_autoincrements = None - - if stmt._supplemental_returning: - supplemental_returning = set(stmt._supplemental_returning) - else: - supplemental_returning = set() - - compiler_implicit_returning = compiler.implicit_returning - - # TODO - see TODO(return_defaults_columns) below - # cols_in_params = set() - - for c in cols: - # scan through every column in the target table - - col_key = _getattr_col_key(c) - - if col_key in parameters and col_key not in check_columns: - # parameter is present for the column. use that. - - _append_param_parameter( - compiler, - stmt, - compile_state, - c, - col_key, - parameters, - _col_bind_name, - implicit_returning, - implicit_return_defaults, - postfetch_lastrowid, - values, - autoincrement_col, - insert_null_pk_still_autoincrements, - kw, - ) - - # TODO - see TODO(return_defaults_columns) below - # cols_in_params.add(c) - - elif isinsert: - # no parameter is present and it's an insert. - - if c.primary_key and need_pks: - # it's a primary key column, it will need to be generated by a - # default generator of some kind, and the statement expects - # inserted_primary_key to be available. - - if implicit_returning: - # we can use RETURNING, find out how to invoke this - # column and get the value where RETURNING is an option. - # we can inline server-side functions in this case. - - _append_param_insert_pk_returning( - compiler, stmt, c, values, kw - ) - else: - # otherwise, find out how to invoke this column - # and get its value where RETURNING is not an option. - # if we have to invoke a server-side function, we need - # to pre-execute it. or if this is a straight - # autoincrement column and the dialect supports it - # we can use cursor.lastrowid. - - _append_param_insert_pk_no_returning( - compiler, stmt, c, values, kw - ) - - elif c.default is not None: - # column has a default, but it's not a pk column, or it is but - # we don't need to get the pk back. - if not c.default.is_sentinel or ( - use_sentinel_columns is not None - ): - _append_param_insert_hasdefault( - compiler, stmt, c, implicit_return_defaults, values, kw - ) - - elif c.server_default is not None: - # column has a DDL-level default, and is either not a pk - # column or we don't need the pk. - if implicit_return_defaults and c in implicit_return_defaults: - compiler_implicit_returning.append(c) - elif not c.primary_key: - compiler.postfetch.append(c) - - elif implicit_return_defaults and c in implicit_return_defaults: - compiler_implicit_returning.append(c) - - elif ( - c.primary_key - and c is not stmt.table._autoincrement_column - and not c.nullable - ): - _warn_pk_with_no_anticipated_value(c) - - elif compile_state.isupdate: - # no parameter is present and it's an insert. - - _append_param_update( - compiler, - compile_state, - stmt, - c, - implicit_return_defaults, - values, - kw, - ) - - # adding supplemental cols to implicit_returning in table - # order so that order is maintained between multiple INSERT - # statements which may have different parameters included, but all - # have the same RETURNING clause - if ( - c in supplemental_returning - and c not in compiler_implicit_returning - ): - compiler_implicit_returning.append(c) - - if supplemental_returning: - # we should have gotten every col into implicit_returning, - # however supplemental returning can also have SQL functions etc. - # in it - remaining_supplemental = supplemental_returning.difference( - compiler_implicit_returning - ) - compiler_implicit_returning.extend( - c - for c in stmt._supplemental_returning - if c in remaining_supplemental - ) - - # TODO(return_defaults_columns): there can still be more columns in - # _return_defaults_columns in the case that they are from something like an - # aliased of the table. we can add them here, however this breaks other ORM - # things. so this is for another day. see - # test/orm/dml/test_update_delete_where.py -> test_update_from_alias - - # if stmt._return_defaults_columns: - # compiler_implicit_returning.extend( - # set(stmt._return_defaults_columns) - # .difference(compiler_implicit_returning) - # .difference(cols_in_params) - # ) - - return (use_insertmanyvalues, use_sentinel_columns) - - -def _setup_delete_return_defaults( - compiler, - stmt, - compile_state, - parameters, - _getattr_col_key, - _column_as_key, - _col_bind_name, - check_columns, - values, - toplevel, - kw, -): - (_, _, implicit_return_defaults, *_) = _get_returning_modifiers( - compiler, stmt, compile_state, toplevel - ) - - if not implicit_return_defaults: - return - - if stmt._return_defaults_columns: - compiler.implicit_returning.extend(implicit_return_defaults) - - if stmt._supplemental_returning: - ir_set = set(compiler.implicit_returning) - compiler.implicit_returning.extend( - c for c in stmt._supplemental_returning if c not in ir_set - ) - - -def _append_param_parameter( - compiler, - stmt, - compile_state, - c, - col_key, - parameters, - _col_bind_name, - implicit_returning, - implicit_return_defaults, - postfetch_lastrowid, - values, - autoincrement_col, - insert_null_pk_still_autoincrements, - kw, -): - value = parameters.pop(col_key) - - col_value = compiler.preparer.format_column( - c, use_table=compile_state.include_table_with_column_exprs - ) - - accumulated_bind_names: Set[str] = set() - - if coercions._is_literal(value): - if ( - insert_null_pk_still_autoincrements - and c.primary_key - and c is autoincrement_col - ): - # support use case for #7998, fetch autoincrement cols - # even if value was given. - - if postfetch_lastrowid: - compiler.postfetch_lastrowid = True - elif implicit_returning: - compiler.implicit_returning.append(c) - - value = _create_bind_param( - compiler, - c, - value, - required=value is REQUIRED, - name=( - _col_bind_name(c) - if not _compile_state_isinsert(compile_state) - or not compile_state._has_multi_parameters - else "%s_m0" % _col_bind_name(c) - ), - accumulate_bind_names=accumulated_bind_names, - **kw, - ) - elif value._is_bind_parameter: - if ( - insert_null_pk_still_autoincrements - and value.value is None - and c.primary_key - and c is autoincrement_col - ): - # support use case for #7998, fetch autoincrement cols - # even if value was given - if implicit_returning: - compiler.implicit_returning.append(c) - elif compiler.dialect.postfetch_lastrowid: - compiler.postfetch_lastrowid = True - - value = _handle_values_anonymous_param( - compiler, - c, - value, - name=( - _col_bind_name(c) - if not _compile_state_isinsert(compile_state) - or not compile_state._has_multi_parameters - else "%s_m0" % _col_bind_name(c) - ), - accumulate_bind_names=accumulated_bind_names, - **kw, - ) - else: - # value is a SQL expression - value = compiler.process( - value.self_group(), - accumulate_bind_names=accumulated_bind_names, - **kw, - ) - - if compile_state.isupdate: - if implicit_return_defaults and c in implicit_return_defaults: - compiler.implicit_returning.append(c) - - else: - compiler.postfetch.append(c) - else: - if c.primary_key: - if implicit_returning: - compiler.implicit_returning.append(c) - elif compiler.dialect.postfetch_lastrowid: - compiler.postfetch_lastrowid = True - - elif implicit_return_defaults and (c in implicit_return_defaults): - compiler.implicit_returning.append(c) - - else: - # postfetch specifically means, "we can SELECT the row we just - # inserted by primary key to get back the server generated - # defaults". so by definition this can't be used to get the - # primary key value back, because we need to have it ahead of - # time. - - compiler.postfetch.append(c) - - values.append((c, col_value, value, accumulated_bind_names)) - - -def _append_param_insert_pk_returning(compiler, stmt, c, values, kw): - """Create a primary key expression in the INSERT statement where - we want to populate result.inserted_primary_key and RETURNING - is available. - - """ - if c.default is not None: - if c.default.is_sequence: - if compiler.dialect.supports_sequences and ( - not c.default.optional - or not compiler.dialect.sequences_optional - ): - accumulated_bind_names: Set[str] = set() - values.append( - ( - c, - compiler.preparer.format_column(c), - compiler.process( - c.default, - accumulate_bind_names=accumulated_bind_names, - **kw, - ), - accumulated_bind_names, - ) - ) - compiler.implicit_returning.append(c) - elif c.default.is_clause_element: - accumulated_bind_names = set() - values.append( - ( - c, - compiler.preparer.format_column(c), - compiler.process( - c.default.arg.self_group(), - accumulate_bind_names=accumulated_bind_names, - **kw, - ), - accumulated_bind_names, - ) - ) - compiler.implicit_returning.append(c) - else: - # client side default. OK we can't use RETURNING, need to - # do a "prefetch", which in fact fetches the default value - # on the Python side - values.append( - ( - c, - compiler.preparer.format_column(c), - _create_insert_prefetch_bind_param(compiler, c, **kw), - (c.key,), - ) - ) - elif c is stmt.table._autoincrement_column or c.server_default is not None: - compiler.implicit_returning.append(c) - elif not c.nullable: - # no .default, no .server_default, not autoincrement, we have - # no indication this primary key column will have any value - _warn_pk_with_no_anticipated_value(c) - - -def _append_param_insert_pk_no_returning(compiler, stmt, c, values, kw): - """Create a primary key expression in the INSERT statement where - we want to populate result.inserted_primary_key and we cannot use - RETURNING. - - Depending on the kind of default here we may create a bound parameter - in the INSERT statement and pre-execute a default generation function, - or we may use cursor.lastrowid if supported by the dialect. - - - """ - - if ( - # column has a Python-side default - c.default is not None - and ( - # and it either is not a sequence, or it is and we support - # sequences and want to invoke it - not c.default.is_sequence - or ( - compiler.dialect.supports_sequences - and ( - not c.default.optional - or not compiler.dialect.sequences_optional - ) - ) - ) - ) or ( - # column is the "autoincrement column" - c is stmt.table._autoincrement_column - and ( - # dialect can't use cursor.lastrowid - not compiler.dialect.postfetch_lastrowid - and ( - # column has a Sequence and we support those - ( - c.default is not None - and c.default.is_sequence - and compiler.dialect.supports_sequences - ) - or - # column has no default on it, but dialect can run the - # "autoincrement" mechanism explicitly, e.g. PostgreSQL - # SERIAL we know the sequence name - ( - c.default is None - and compiler.dialect.preexecute_autoincrement_sequences - ) - ) - ) - ): - # do a pre-execute of the default - values.append( - ( - c, - compiler.preparer.format_column(c), - _create_insert_prefetch_bind_param(compiler, c, **kw), - (c.key,), - ) - ) - elif ( - c.default is None - and c.server_default is None - and not c.nullable - and c is not stmt.table._autoincrement_column - ): - # no .default, no .server_default, not autoincrement, we have - # no indication this primary key column will have any value - _warn_pk_with_no_anticipated_value(c) - elif compiler.dialect.postfetch_lastrowid: - # finally, where it seems like there will be a generated primary key - # value and we haven't set up any other way to fetch it, and the - # dialect supports cursor.lastrowid, switch on the lastrowid flag so - # that the DefaultExecutionContext calls upon cursor.lastrowid - compiler.postfetch_lastrowid = True - - -def _append_param_insert_hasdefault( - compiler, stmt, c, implicit_return_defaults, values, kw -): - if c.default.is_sequence: - if compiler.dialect.supports_sequences and ( - not c.default.optional or not compiler.dialect.sequences_optional - ): - accumulated_bind_names: Set[str] = set() - values.append( - ( - c, - compiler.preparer.format_column(c), - compiler.process( - c.default, - accumulate_bind_names=accumulated_bind_names, - **kw, - ), - accumulated_bind_names, - ) - ) - if implicit_return_defaults and c in implicit_return_defaults: - compiler.implicit_returning.append(c) - elif not c.primary_key: - compiler.postfetch.append(c) - elif c.default.is_clause_element: - accumulated_bind_names = set() - values.append( - ( - c, - compiler.preparer.format_column(c), - compiler.process( - c.default.arg.self_group(), - accumulate_bind_names=accumulated_bind_names, - **kw, - ), - accumulated_bind_names, - ) - ) - - if implicit_return_defaults and c in implicit_return_defaults: - compiler.implicit_returning.append(c) - elif not c.primary_key: - # don't add primary key column to postfetch - compiler.postfetch.append(c) - else: - values.append( - ( - c, - compiler.preparer.format_column(c), - _create_insert_prefetch_bind_param(compiler, c, **kw), - (c.key,), - ) - ) - - -def _append_param_insert_select_hasdefault( - compiler: SQLCompiler, - stmt: ValuesBase, - c: ColumnClause[Any], - values: List[_CrudParamElementSQLExpr], - kw: Dict[str, Any], -) -> None: - if default_is_sequence(c.default): - if compiler.dialect.supports_sequences and ( - not c.default.optional or not compiler.dialect.sequences_optional - ): - values.append( - ( - c, - compiler.preparer.format_column(c), - c.default.next_value(), - (), - ) - ) - elif default_is_clause_element(c.default): - values.append( - ( - c, - compiler.preparer.format_column(c), - c.default.arg.self_group(), - (), - ) - ) - else: - values.append( - ( - c, - compiler.preparer.format_column(c), - _create_insert_prefetch_bind_param( - compiler, c, process=False, **kw - ), - (c.key,), - ) - ) - - -def _append_param_update( - compiler, compile_state, stmt, c, implicit_return_defaults, values, kw -): - include_table = compile_state.include_table_with_column_exprs - if c.onupdate is not None and not c.onupdate.is_sequence: - if c.onupdate.is_clause_element: - values.append( - ( - c, - compiler.preparer.format_column( - c, - use_table=include_table, - ), - compiler.process(c.onupdate.arg.self_group(), **kw), - (), - ) - ) - if implicit_return_defaults and c in implicit_return_defaults: - compiler.implicit_returning.append(c) - else: - compiler.postfetch.append(c) - else: - values.append( - ( - c, - compiler.preparer.format_column( - c, - use_table=include_table, - ), - _create_update_prefetch_bind_param(compiler, c, **kw), - (c.key,), - ) - ) - elif c.server_onupdate is not None: - if implicit_return_defaults and c in implicit_return_defaults: - compiler.implicit_returning.append(c) - else: - compiler.postfetch.append(c) - elif ( - implicit_return_defaults - and (stmt._return_defaults_columns or not stmt._return_defaults) - and c in implicit_return_defaults - ): - compiler.implicit_returning.append(c) - - -@overload -def _create_insert_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: Literal[True] = ..., - **kw: Any, -) -> str: ... - - -@overload -def _create_insert_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: Literal[False], - **kw: Any, -) -> elements.BindParameter[Any]: ... - - -def _create_insert_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: bool = True, - name: Optional[str] = None, - **kw: Any, -) -> Union[elements.BindParameter[Any], str]: - param = _create_bind_param( - compiler, c, None, process=process, name=name, **kw - ) - compiler.insert_prefetch.append(c) # type: ignore - return param - - -@overload -def _create_update_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: Literal[True] = ..., - **kw: Any, -) -> str: ... - - -@overload -def _create_update_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: Literal[False], - **kw: Any, -) -> elements.BindParameter[Any]: ... - - -def _create_update_prefetch_bind_param( - compiler: SQLCompiler, - c: ColumnElement[Any], - process: bool = True, - name: Optional[str] = None, - **kw: Any, -) -> Union[elements.BindParameter[Any], str]: - param = _create_bind_param( - compiler, c, None, process=process, name=name, **kw - ) - compiler.update_prefetch.append(c) # type: ignore - return param - - -class _multiparam_column(elements.ColumnElement[Any]): - _is_multiparam_column = True - - def __init__(self, original, index): - self.index = index - self.key = "%s_m%d" % (original.key, index + 1) - self.original = original - self.default = original.default - self.type = original.type - - def compare(self, other, **kw): - raise NotImplementedError() - - def _copy_internals(self, other, **kw): - raise NotImplementedError() - - def __eq__(self, other): - return ( - isinstance(other, _multiparam_column) - and other.key == self.key - and other.original == self.original - ) - - @util.memoized_property - def _default_description_tuple(self) -> _DefaultDescriptionTuple: - """used by default.py -> _process_execute_defaults()""" - - return _DefaultDescriptionTuple._from_column_default(self.default) - - @util.memoized_property - def _onupdate_description_tuple(self) -> _DefaultDescriptionTuple: - """used by default.py -> _process_execute_defaults()""" - - return _DefaultDescriptionTuple._from_column_default(self.onupdate) - - -def _process_multiparam_default_bind( - compiler: SQLCompiler, - stmt: ValuesBase, - c: KeyedColumnElement[Any], - index: int, - kw: Dict[str, Any], -) -> str: - if not c.default: - raise exc.CompileError( - "INSERT value for column %s is explicitly rendered as a bound" - "parameter in the VALUES clause; " - "a Python-side value or SQL expression is required" % c - ) - elif default_is_clause_element(c.default): - return compiler.process(c.default.arg.self_group(), **kw) - elif c.default.is_sequence: - # these conditions would have been established - # by append_param_insert_(?:hasdefault|pk_returning|pk_no_returning) - # in order for us to be here, so these don't need to be - # checked - # assert compiler.dialect.supports_sequences and ( - # not c.default.optional - # or not compiler.dialect.sequences_optional - # ) - return compiler.process(c.default, **kw) - else: - col = _multiparam_column(c, index) - assert isinstance(stmt, dml.Insert) - return _create_insert_prefetch_bind_param( - compiler, col, process=True, **kw - ) - - -def _get_update_multitable_params( - compiler, - stmt, - compile_state, - stmt_parameter_tuples, - check_columns, - _col_bind_name, - _getattr_col_key, - values, - kw, -): - normalized_params = { - coercions.expect(roles.DMLColumnRole, c): param - for c, param in stmt_parameter_tuples or () - } - - include_table = compile_state.include_table_with_column_exprs - - affected_tables = set() - for t in compile_state._extra_froms: - for c in t.c: - if c in normalized_params: - affected_tables.add(t) - check_columns[_getattr_col_key(c)] = c - value = normalized_params[c] - - col_value = compiler.process(c, include_table=include_table) - if coercions._is_literal(value): - value = _create_bind_param( - compiler, - c, - value, - required=value is REQUIRED, - name=_col_bind_name(c), - **kw, # TODO: no test coverage for literal binds here - ) - accumulated_bind_names: Iterable[str] = (c.key,) - elif value._is_bind_parameter: - cbn = _col_bind_name(c) - value = _handle_values_anonymous_param( - compiler, c, value, name=cbn, **kw - ) - accumulated_bind_names = (cbn,) - else: - compiler.postfetch.append(c) - value = compiler.process(value.self_group(), **kw) - accumulated_bind_names = () - values.append((c, col_value, value, accumulated_bind_names)) - # determine tables which are actually to be updated - process onupdate - # and server_onupdate for these - for t in affected_tables: - for c in t.c: - if c in normalized_params: - continue - elif c.onupdate is not None and not c.onupdate.is_sequence: - if c.onupdate.is_clause_element: - values.append( - ( - c, - compiler.process(c, include_table=include_table), - compiler.process( - c.onupdate.arg.self_group(), **kw - ), - (), - ) - ) - compiler.postfetch.append(c) - else: - values.append( - ( - c, - compiler.process(c, include_table=include_table), - _create_update_prefetch_bind_param( - compiler, c, name=_col_bind_name(c), **kw - ), - (c.key,), - ) - ) - elif c.server_onupdate is not None: - compiler.postfetch.append(c) - - -def _extend_values_for_multiparams( - compiler: SQLCompiler, - stmt: ValuesBase, - compile_state: DMLState, - initial_values: Sequence[_CrudParamElementStr], - _column_as_key: Callable[..., str], - kw: Dict[str, Any], -) -> List[Sequence[_CrudParamElementStr]]: - values_0 = initial_values - values = [initial_values] - - mp = compile_state._multi_parameters - assert mp is not None - for i, row in enumerate(mp[1:]): - extension: List[_CrudParamElementStr] = [] - - row = {_column_as_key(key): v for key, v in row.items()} - - for col, col_expr, param, accumulated_names in values_0: - if col.key in row: - key = col.key - - if coercions._is_literal(row[key]): - new_param = _create_bind_param( - compiler, - col, - row[key], - name="%s_m%d" % (col.key, i + 1), - **kw, - ) - else: - new_param = compiler.process(row[key].self_group(), **kw) - else: - new_param = _process_multiparam_default_bind( - compiler, stmt, col, i, kw - ) - - extension.append((col, col_expr, new_param, accumulated_names)) - - values.append(extension) - - return values - - -def _get_stmt_parameter_tuples_params( - compiler, - compile_state, - parameters, - stmt_parameter_tuples, - _column_as_key, - values, - kw, -): - for k, v in stmt_parameter_tuples: - colkey = _column_as_key(k) - if colkey is not None: - parameters.setdefault(colkey, v) - else: - # a non-Column expression on the left side; - # add it to values() in an "as-is" state, - # coercing right side to bound param - - # note one of the main use cases for this is array slice - # updates on PostgreSQL, as the left side is also an expression. - - col_expr = compiler.process( - k, include_table=compile_state.include_table_with_column_exprs - ) - - if coercions._is_literal(v): - v = compiler.process( - elements.BindParameter(None, v, type_=k.type), **kw - ) - else: - if v._is_bind_parameter and v.type._isnull: - # either unique parameter, or other bound parameters that - # were passed in directly - # set type to that of the column unconditionally - v = v._with_binary_element_type(k.type) - - v = compiler.process(v.self_group(), **kw) - - # TODO: not sure if accumulated_bind_names applies here - values.append((k, col_expr, v, ())) - - -def _get_returning_modifiers(compiler, stmt, compile_state, toplevel): - """determines RETURNING strategy, if any, for the statement. - - This is where it's determined what we need to fetch from the - INSERT or UPDATE statement after it's invoked. - - """ - - dialect = compiler.dialect - - need_pks = ( - toplevel - and _compile_state_isinsert(compile_state) - and not stmt._inline - and ( - not compiler.for_executemany - or (dialect.insert_executemany_returning and stmt._return_defaults) - ) - and not stmt._returning - # and (not stmt._returning or stmt._return_defaults) - and not compile_state._has_multi_parameters - ) - - # check if we have access to simple cursor.lastrowid. we can use that - # after the INSERT if that's all we need. - postfetch_lastrowid = ( - need_pks - and dialect.postfetch_lastrowid - and stmt.table._autoincrement_column is not None - ) - - # see if we want to add RETURNING to an INSERT in order to get - # primary key columns back. This would be instead of postfetch_lastrowid - # if that's set. - implicit_returning = ( - # statement itself can veto it - need_pks - # the dialect can veto it if it just doesnt support RETURNING - # with INSERT - and dialect.insert_returning - # user-defined implicit_returning on Table can veto it - and compile_state._primary_table.implicit_returning - # the compile_state can veto it (SQlite uses this to disable - # RETURNING for an ON CONFLICT insert, as SQLite does not return - # for rows that were updated, which is wrong) - and compile_state._supports_implicit_returning - and ( - # since we support MariaDB and SQLite which also support lastrowid, - # decide if we should use lastrowid or RETURNING. for insert - # that didnt call return_defaults() and has just one set of - # parameters, we can use lastrowid. this is more "traditional" - # and a lot of weird use cases are supported by it. - # SQLite lastrowid times 3x faster than returning, - # Mariadb lastrowid 2x faster than returning - (not postfetch_lastrowid or dialect.favor_returning_over_lastrowid) - or compile_state._has_multi_parameters - or stmt._return_defaults - ) - ) - if implicit_returning: - postfetch_lastrowid = False - - if _compile_state_isinsert(compile_state): - should_implicit_return_defaults = ( - implicit_returning and stmt._return_defaults - ) - explicit_returning = ( - should_implicit_return_defaults - or stmt._returning - or stmt._supplemental_returning - ) - use_insertmanyvalues = ( - toplevel - and compiler.for_executemany - and dialect.use_insertmanyvalues - and ( - explicit_returning or dialect.use_insertmanyvalues_wo_returning - ) - ) - - use_sentinel_columns = None - if ( - use_insertmanyvalues - and explicit_returning - and stmt._sort_by_parameter_order - ): - use_sentinel_columns = compiler._get_sentinel_column_for_table( - stmt.table - ) - - elif compile_state.isupdate: - should_implicit_return_defaults = ( - stmt._return_defaults - and compile_state._primary_table.implicit_returning - and compile_state._supports_implicit_returning - and dialect.update_returning - ) - use_insertmanyvalues = False - use_sentinel_columns = None - elif compile_state.isdelete: - should_implicit_return_defaults = ( - stmt._return_defaults - and compile_state._primary_table.implicit_returning - and compile_state._supports_implicit_returning - and dialect.delete_returning - ) - use_insertmanyvalues = False - use_sentinel_columns = None - else: - should_implicit_return_defaults = False # pragma: no cover - use_insertmanyvalues = False - use_sentinel_columns = None - - if should_implicit_return_defaults: - if not stmt._return_defaults_columns: - # TODO: this is weird. See #9685 where we have to - # take an extra step to prevent this from happening. why - # would this ever be *all* columns? but if we set to blank, then - # that seems to break things also in the ORM. So we should - # try to clean this up and figure out what return_defaults - # needs to do w/ the ORM etc. here - implicit_return_defaults = set(stmt.table.c) - else: - implicit_return_defaults = set(stmt._return_defaults_columns) - else: - implicit_return_defaults = None - - return ( - need_pks, - implicit_returning or should_implicit_return_defaults, - implicit_return_defaults, - postfetch_lastrowid, - use_insertmanyvalues, - use_sentinel_columns, - ) - - -def _warn_pk_with_no_anticipated_value(c): - msg = ( - "Column '%s.%s' is marked as a member of the " - "primary key for table '%s', " - "but has no Python-side or server-side default generator indicated, " - "nor does it indicate 'autoincrement=True' or 'nullable=True', " - "and no explicit value is passed. " - "Primary key columns typically may not store NULL." - % (c.table.fullname, c.name, c.table.fullname) - ) - if len(c.table.primary_key) > 1: - msg += ( - " Note that as of SQLAlchemy 1.1, 'autoincrement=True' must be " - "indicated explicitly for composite (e.g. multicolumn) primary " - "keys if AUTO_INCREMENT/SERIAL/IDENTITY " - "behavior is expected for one of the columns in the primary key. " - "CREATE TABLE statements are impacted by this change as well on " - "most backends." - ) - util.warn(msg) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/ddl.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/ddl.py deleted file mode 100644 index d9e3f67..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/ddl.py +++ /dev/null @@ -1,1378 +0,0 @@ -# sql/ddl.py -# Copyright (C) 2009-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -""" -Provides the hierarchy of DDL-defining schema items as well as routines -to invoke them for a create/drop call. - -""" -from __future__ import annotations - -import contextlib -import typing -from typing import Any -from typing import Callable -from typing import Iterable -from typing import List -from typing import Optional -from typing import Sequence as typing_Sequence -from typing import Tuple - -from . import roles -from .base import _generative -from .base import Executable -from .base import SchemaVisitor -from .elements import ClauseElement -from .. import exc -from .. import util -from ..util import topological -from ..util.typing import Protocol -from ..util.typing import Self - -if typing.TYPE_CHECKING: - from .compiler import Compiled - from .compiler import DDLCompiler - from .elements import BindParameter - from .schema import Constraint - from .schema import ForeignKeyConstraint - from .schema import SchemaItem - from .schema import Sequence - from .schema import Table - from .selectable import TableClause - from ..engine.base import Connection - from ..engine.interfaces import CacheStats - from ..engine.interfaces import CompiledCacheType - from ..engine.interfaces import Dialect - from ..engine.interfaces import SchemaTranslateMapType - - -class BaseDDLElement(ClauseElement): - """The root of DDL constructs, including those that are sub-elements - within the "create table" and other processes. - - .. versionadded:: 2.0 - - """ - - _hierarchy_supports_caching = False - """disable cache warnings for all _DDLCompiles subclasses. """ - - def _compiler(self, dialect, **kw): - """Return a compiler appropriate for this ClauseElement, given a - Dialect.""" - - return dialect.ddl_compiler(dialect, self, **kw) - - def _compile_w_cache( - self, - dialect: Dialect, - *, - compiled_cache: Optional[CompiledCacheType], - column_keys: List[str], - for_executemany: bool = False, - schema_translate_map: Optional[SchemaTranslateMapType] = None, - **kw: Any, - ) -> Tuple[ - Compiled, Optional[typing_Sequence[BindParameter[Any]]], CacheStats - ]: - raise NotImplementedError() - - -class DDLIfCallable(Protocol): - def __call__( - self, - ddl: BaseDDLElement, - target: SchemaItem, - bind: Optional[Connection], - tables: Optional[List[Table]] = None, - state: Optional[Any] = None, - *, - dialect: Dialect, - compiler: Optional[DDLCompiler] = ..., - checkfirst: bool, - ) -> bool: ... - - -class DDLIf(typing.NamedTuple): - dialect: Optional[str] - callable_: Optional[DDLIfCallable] - state: Optional[Any] - - def _should_execute( - self, - ddl: BaseDDLElement, - target: SchemaItem, - bind: Optional[Connection], - compiler: Optional[DDLCompiler] = None, - **kw: Any, - ) -> bool: - if bind is not None: - dialect = bind.dialect - elif compiler is not None: - dialect = compiler.dialect - else: - assert False, "compiler or dialect is required" - - if isinstance(self.dialect, str): - if self.dialect != dialect.name: - return False - elif isinstance(self.dialect, (tuple, list, set)): - if dialect.name not in self.dialect: - return False - if self.callable_ is not None and not self.callable_( - ddl, - target, - bind, - state=self.state, - dialect=dialect, - compiler=compiler, - **kw, - ): - return False - - return True - - -class ExecutableDDLElement(roles.DDLRole, Executable, BaseDDLElement): - """Base class for standalone executable DDL expression constructs. - - This class is the base for the general purpose :class:`.DDL` class, - as well as the various create/drop clause constructs such as - :class:`.CreateTable`, :class:`.DropTable`, :class:`.AddConstraint`, - etc. - - .. versionchanged:: 2.0 :class:`.ExecutableDDLElement` is renamed from - :class:`.DDLElement`, which still exists for backwards compatibility. - - :class:`.ExecutableDDLElement` integrates closely with SQLAlchemy events, - introduced in :ref:`event_toplevel`. An instance of one is - itself an event receiving callable:: - - event.listen( - users, - 'after_create', - AddConstraint(constraint).execute_if(dialect='postgresql') - ) - - .. seealso:: - - :class:`.DDL` - - :class:`.DDLEvents` - - :ref:`event_toplevel` - - :ref:`schema_ddl_sequences` - - """ - - _ddl_if: Optional[DDLIf] = None - target: Optional[SchemaItem] = None - - def _execute_on_connection( - self, connection, distilled_params, execution_options - ): - return connection._execute_ddl( - self, distilled_params, execution_options - ) - - @_generative - def against(self, target: SchemaItem) -> Self: - """Return a copy of this :class:`_schema.ExecutableDDLElement` which - will include the given target. - - This essentially applies the given item to the ``.target`` attribute of - the returned :class:`_schema.ExecutableDDLElement` object. This target - is then usable by event handlers and compilation routines in order to - provide services such as tokenization of a DDL string in terms of a - particular :class:`_schema.Table`. - - When a :class:`_schema.ExecutableDDLElement` object is established as - an event handler for the :meth:`_events.DDLEvents.before_create` or - :meth:`_events.DDLEvents.after_create` events, and the event then - occurs for a given target such as a :class:`_schema.Constraint` or - :class:`_schema.Table`, that target is established with a copy of the - :class:`_schema.ExecutableDDLElement` object using this method, which - then proceeds to the :meth:`_schema.ExecutableDDLElement.execute` - method in order to invoke the actual DDL instruction. - - :param target: a :class:`_schema.SchemaItem` that will be the subject - of a DDL operation. - - :return: a copy of this :class:`_schema.ExecutableDDLElement` with the - ``.target`` attribute assigned to the given - :class:`_schema.SchemaItem`. - - .. seealso:: - - :class:`_schema.DDL` - uses tokenization against the "target" when - processing the DDL string. - - """ - self.target = target - return self - - @_generative - def execute_if( - self, - dialect: Optional[str] = None, - callable_: Optional[DDLIfCallable] = None, - state: Optional[Any] = None, - ) -> Self: - r"""Return a callable that will execute this - :class:`_ddl.ExecutableDDLElement` conditionally within an event - handler. - - Used to provide a wrapper for event listening:: - - event.listen( - metadata, - 'before_create', - DDL("my_ddl").execute_if(dialect='postgresql') - ) - - :param dialect: May be a string or tuple of strings. - If a string, it will be compared to the name of the - executing database dialect:: - - DDL('something').execute_if(dialect='postgresql') - - If a tuple, specifies multiple dialect names:: - - DDL('something').execute_if(dialect=('postgresql', 'mysql')) - - :param callable\_: A callable, which will be invoked with - three positional arguments as well as optional keyword - arguments: - - :ddl: - This DDL element. - - :target: - The :class:`_schema.Table` or :class:`_schema.MetaData` - object which is the - target of this event. May be None if the DDL is executed - explicitly. - - :bind: - The :class:`_engine.Connection` being used for DDL execution. - May be None if this construct is being created inline within - a table, in which case ``compiler`` will be present. - - :tables: - Optional keyword argument - a list of Table objects which are to - be created/ dropped within a MetaData.create_all() or drop_all() - method call. - - :dialect: keyword argument, but always present - the - :class:`.Dialect` involved in the operation. - - :compiler: keyword argument. Will be ``None`` for an engine - level DDL invocation, but will refer to a :class:`.DDLCompiler` - if this DDL element is being created inline within a table. - - :state: - Optional keyword argument - will be the ``state`` argument - passed to this function. - - :checkfirst: - Keyword argument, will be True if the 'checkfirst' flag was - set during the call to ``create()``, ``create_all()``, - ``drop()``, ``drop_all()``. - - If the callable returns a True value, the DDL statement will be - executed. - - :param state: any value which will be passed to the callable\_ - as the ``state`` keyword argument. - - .. seealso:: - - :meth:`.SchemaItem.ddl_if` - - :class:`.DDLEvents` - - :ref:`event_toplevel` - - """ - self._ddl_if = DDLIf(dialect, callable_, state) - return self - - def _should_execute(self, target, bind, **kw): - if self._ddl_if is None: - return True - else: - return self._ddl_if._should_execute(self, target, bind, **kw) - - def _invoke_with(self, bind): - if self._should_execute(self.target, bind): - return bind.execute(self) - - def __call__(self, target, bind, **kw): - """Execute the DDL as a ddl_listener.""" - - self.against(target)._invoke_with(bind) - - def _generate(self): - s = self.__class__.__new__(self.__class__) - s.__dict__ = self.__dict__.copy() - return s - - -DDLElement = ExecutableDDLElement -""":class:`.DDLElement` is renamed to :class:`.ExecutableDDLElement`.""" - - -class DDL(ExecutableDDLElement): - """A literal DDL statement. - - Specifies literal SQL DDL to be executed by the database. DDL objects - function as DDL event listeners, and can be subscribed to those events - listed in :class:`.DDLEvents`, using either :class:`_schema.Table` or - :class:`_schema.MetaData` objects as targets. - Basic templating support allows - a single DDL instance to handle repetitive tasks for multiple tables. - - Examples:: - - from sqlalchemy import event, DDL - - tbl = Table('users', metadata, Column('uid', Integer)) - event.listen(tbl, 'before_create', DDL('DROP TRIGGER users_trigger')) - - spow = DDL('ALTER TABLE %(table)s SET secretpowers TRUE') - event.listen(tbl, 'after_create', spow.execute_if(dialect='somedb')) - - drop_spow = DDL('ALTER TABLE users SET secretpowers FALSE') - connection.execute(drop_spow) - - When operating on Table events, the following ``statement`` - string substitutions are available:: - - %(table)s - the Table name, with any required quoting applied - %(schema)s - the schema name, with any required quoting applied - %(fullname)s - the Table name including schema, quoted if needed - - The DDL's "context", if any, will be combined with the standard - substitutions noted above. Keys present in the context will override - the standard substitutions. - - """ - - __visit_name__ = "ddl" - - def __init__(self, statement, context=None): - """Create a DDL statement. - - :param statement: - A string or unicode string to be executed. Statements will be - processed with Python's string formatting operator using - a fixed set of string substitutions, as well as additional - substitutions provided by the optional :paramref:`.DDL.context` - parameter. - - A literal '%' in a statement must be escaped as '%%'. - - SQL bind parameters are not available in DDL statements. - - :param context: - Optional dictionary, defaults to None. These values will be - available for use in string substitutions on the DDL statement. - - .. seealso:: - - :class:`.DDLEvents` - - :ref:`event_toplevel` - - """ - - if not isinstance(statement, str): - raise exc.ArgumentError( - "Expected a string or unicode SQL statement, got '%r'" - % statement - ) - - self.statement = statement - self.context = context or {} - - def __repr__(self): - parts = [repr(self.statement)] - if self.context: - parts.append(f"context={self.context}") - - return "<%s@%s; %s>" % ( - type(self).__name__, - id(self), - ", ".join(parts), - ) - - -class _CreateDropBase(ExecutableDDLElement): - """Base class for DDL constructs that represent CREATE and DROP or - equivalents. - - The common theme of _CreateDropBase is a single - ``element`` attribute which refers to the element - to be created or dropped. - - """ - - def __init__( - self, - element, - ): - self.element = self.target = element - self._ddl_if = getattr(element, "_ddl_if", None) - - @property - def stringify_dialect(self): - return self.element.create_drop_stringify_dialect - - def _create_rule_disable(self, compiler): - """Allow disable of _create_rule using a callable. - - Pass to _create_rule using - util.portable_instancemethod(self._create_rule_disable) - to retain serializability. - - """ - return False - - -class _CreateBase(_CreateDropBase): - def __init__(self, element, if_not_exists=False): - super().__init__(element) - self.if_not_exists = if_not_exists - - -class _DropBase(_CreateDropBase): - def __init__(self, element, if_exists=False): - super().__init__(element) - self.if_exists = if_exists - - -class CreateSchema(_CreateBase): - """Represent a CREATE SCHEMA statement. - - The argument here is the string name of the schema. - - """ - - __visit_name__ = "create_schema" - - stringify_dialect = "default" - - def __init__( - self, - name, - if_not_exists=False, - ): - """Create a new :class:`.CreateSchema` construct.""" - - super().__init__(element=name, if_not_exists=if_not_exists) - - -class DropSchema(_DropBase): - """Represent a DROP SCHEMA statement. - - The argument here is the string name of the schema. - - """ - - __visit_name__ = "drop_schema" - - stringify_dialect = "default" - - def __init__( - self, - name, - cascade=False, - if_exists=False, - ): - """Create a new :class:`.DropSchema` construct.""" - - super().__init__(element=name, if_exists=if_exists) - self.cascade = cascade - - -class CreateTable(_CreateBase): - """Represent a CREATE TABLE statement.""" - - __visit_name__ = "create_table" - - def __init__( - self, - element: Table, - include_foreign_key_constraints: Optional[ - typing_Sequence[ForeignKeyConstraint] - ] = None, - if_not_exists: bool = False, - ): - """Create a :class:`.CreateTable` construct. - - :param element: a :class:`_schema.Table` that's the subject - of the CREATE - :param on: See the description for 'on' in :class:`.DDL`. - :param include_foreign_key_constraints: optional sequence of - :class:`_schema.ForeignKeyConstraint` objects that will be included - inline within the CREATE construct; if omitted, all foreign key - constraints that do not specify use_alter=True are included. - - :param if_not_exists: if True, an IF NOT EXISTS operator will be - applied to the construct. - - .. versionadded:: 1.4.0b2 - - """ - super().__init__(element, if_not_exists=if_not_exists) - self.columns = [CreateColumn(column) for column in element.columns] - self.include_foreign_key_constraints = include_foreign_key_constraints - - -class _DropView(_DropBase): - """Semi-public 'DROP VIEW' construct. - - Used by the test suite for dialect-agnostic drops of views. - This object will eventually be part of a public "view" API. - - """ - - __visit_name__ = "drop_view" - - -class CreateConstraint(BaseDDLElement): - def __init__(self, element: Constraint): - self.element = element - - -class CreateColumn(BaseDDLElement): - """Represent a :class:`_schema.Column` - as rendered in a CREATE TABLE statement, - via the :class:`.CreateTable` construct. - - This is provided to support custom column DDL within the generation - of CREATE TABLE statements, by using the - compiler extension documented in :ref:`sqlalchemy.ext.compiler_toplevel` - to extend :class:`.CreateColumn`. - - Typical integration is to examine the incoming :class:`_schema.Column` - object, and to redirect compilation if a particular flag or condition - is found:: - - from sqlalchemy import schema - from sqlalchemy.ext.compiler import compiles - - @compiles(schema.CreateColumn) - def compile(element, compiler, **kw): - column = element.element - - if "special" not in column.info: - return compiler.visit_create_column(element, **kw) - - text = "%s SPECIAL DIRECTIVE %s" % ( - column.name, - compiler.type_compiler.process(column.type) - ) - default = compiler.get_column_default_string(column) - if default is not None: - text += " DEFAULT " + default - - if not column.nullable: - text += " NOT NULL" - - if column.constraints: - text += " ".join( - compiler.process(const) - for const in column.constraints) - return text - - The above construct can be applied to a :class:`_schema.Table` - as follows:: - - from sqlalchemy import Table, Metadata, Column, Integer, String - from sqlalchemy import schema - - metadata = MetaData() - - table = Table('mytable', MetaData(), - Column('x', Integer, info={"special":True}, primary_key=True), - Column('y', String(50)), - Column('z', String(20), info={"special":True}) - ) - - metadata.create_all(conn) - - Above, the directives we've added to the :attr:`_schema.Column.info` - collection - will be detected by our custom compilation scheme:: - - CREATE TABLE mytable ( - x SPECIAL DIRECTIVE INTEGER NOT NULL, - y VARCHAR(50), - z SPECIAL DIRECTIVE VARCHAR(20), - PRIMARY KEY (x) - ) - - The :class:`.CreateColumn` construct can also be used to skip certain - columns when producing a ``CREATE TABLE``. This is accomplished by - creating a compilation rule that conditionally returns ``None``. - This is essentially how to produce the same effect as using the - ``system=True`` argument on :class:`_schema.Column`, which marks a column - as an implicitly-present "system" column. - - For example, suppose we wish to produce a :class:`_schema.Table` - which skips - rendering of the PostgreSQL ``xmin`` column against the PostgreSQL - backend, but on other backends does render it, in anticipation of a - triggered rule. A conditional compilation rule could skip this name only - on PostgreSQL:: - - from sqlalchemy.schema import CreateColumn - - @compiles(CreateColumn, "postgresql") - def skip_xmin(element, compiler, **kw): - if element.element.name == 'xmin': - return None - else: - return compiler.visit_create_column(element, **kw) - - - my_table = Table('mytable', metadata, - Column('id', Integer, primary_key=True), - Column('xmin', Integer) - ) - - Above, a :class:`.CreateTable` construct will generate a ``CREATE TABLE`` - which only includes the ``id`` column in the string; the ``xmin`` column - will be omitted, but only against the PostgreSQL backend. - - """ - - __visit_name__ = "create_column" - - def __init__(self, element): - self.element = element - - -class DropTable(_DropBase): - """Represent a DROP TABLE statement.""" - - __visit_name__ = "drop_table" - - def __init__(self, element: Table, if_exists: bool = False): - """Create a :class:`.DropTable` construct. - - :param element: a :class:`_schema.Table` that's the subject - of the DROP. - :param on: See the description for 'on' in :class:`.DDL`. - :param if_exists: if True, an IF EXISTS operator will be applied to the - construct. - - .. versionadded:: 1.4.0b2 - - """ - super().__init__(element, if_exists=if_exists) - - -class CreateSequence(_CreateBase): - """Represent a CREATE SEQUENCE statement.""" - - __visit_name__ = "create_sequence" - - def __init__(self, element: Sequence, if_not_exists: bool = False): - super().__init__(element, if_not_exists=if_not_exists) - - -class DropSequence(_DropBase): - """Represent a DROP SEQUENCE statement.""" - - __visit_name__ = "drop_sequence" - - def __init__(self, element: Sequence, if_exists: bool = False): - super().__init__(element, if_exists=if_exists) - - -class CreateIndex(_CreateBase): - """Represent a CREATE INDEX statement.""" - - __visit_name__ = "create_index" - - def __init__(self, element, if_not_exists=False): - """Create a :class:`.Createindex` construct. - - :param element: a :class:`_schema.Index` that's the subject - of the CREATE. - :param if_not_exists: if True, an IF NOT EXISTS operator will be - applied to the construct. - - .. versionadded:: 1.4.0b2 - - """ - super().__init__(element, if_not_exists=if_not_exists) - - -class DropIndex(_DropBase): - """Represent a DROP INDEX statement.""" - - __visit_name__ = "drop_index" - - def __init__(self, element, if_exists=False): - """Create a :class:`.DropIndex` construct. - - :param element: a :class:`_schema.Index` that's the subject - of the DROP. - :param if_exists: if True, an IF EXISTS operator will be applied to the - construct. - - .. versionadded:: 1.4.0b2 - - """ - super().__init__(element, if_exists=if_exists) - - -class AddConstraint(_CreateBase): - """Represent an ALTER TABLE ADD CONSTRAINT statement.""" - - __visit_name__ = "add_constraint" - - def __init__(self, element): - super().__init__(element) - element._create_rule = util.portable_instancemethod( - self._create_rule_disable - ) - - -class DropConstraint(_DropBase): - """Represent an ALTER TABLE DROP CONSTRAINT statement.""" - - __visit_name__ = "drop_constraint" - - def __init__(self, element, cascade=False, if_exists=False, **kw): - self.cascade = cascade - super().__init__(element, if_exists=if_exists, **kw) - element._create_rule = util.portable_instancemethod( - self._create_rule_disable - ) - - -class SetTableComment(_CreateDropBase): - """Represent a COMMENT ON TABLE IS statement.""" - - __visit_name__ = "set_table_comment" - - -class DropTableComment(_CreateDropBase): - """Represent a COMMENT ON TABLE '' statement. - - Note this varies a lot across database backends. - - """ - - __visit_name__ = "drop_table_comment" - - -class SetColumnComment(_CreateDropBase): - """Represent a COMMENT ON COLUMN IS statement.""" - - __visit_name__ = "set_column_comment" - - -class DropColumnComment(_CreateDropBase): - """Represent a COMMENT ON COLUMN IS NULL statement.""" - - __visit_name__ = "drop_column_comment" - - -class SetConstraintComment(_CreateDropBase): - """Represent a COMMENT ON CONSTRAINT IS statement.""" - - __visit_name__ = "set_constraint_comment" - - -class DropConstraintComment(_CreateDropBase): - """Represent a COMMENT ON CONSTRAINT IS NULL statement.""" - - __visit_name__ = "drop_constraint_comment" - - -class InvokeDDLBase(SchemaVisitor): - def __init__(self, connection): - self.connection = connection - - @contextlib.contextmanager - def with_ddl_events(self, target, **kw): - """helper context manager that will apply appropriate DDL events - to a CREATE or DROP operation.""" - - raise NotImplementedError() - - -class InvokeCreateDDLBase(InvokeDDLBase): - @contextlib.contextmanager - def with_ddl_events(self, target, **kw): - """helper context manager that will apply appropriate DDL events - to a CREATE or DROP operation.""" - - target.dispatch.before_create( - target, self.connection, _ddl_runner=self, **kw - ) - yield - target.dispatch.after_create( - target, self.connection, _ddl_runner=self, **kw - ) - - -class InvokeDropDDLBase(InvokeDDLBase): - @contextlib.contextmanager - def with_ddl_events(self, target, **kw): - """helper context manager that will apply appropriate DDL events - to a CREATE or DROP operation.""" - - target.dispatch.before_drop( - target, self.connection, _ddl_runner=self, **kw - ) - yield - target.dispatch.after_drop( - target, self.connection, _ddl_runner=self, **kw - ) - - -class SchemaGenerator(InvokeCreateDDLBase): - def __init__( - self, dialect, connection, checkfirst=False, tables=None, **kwargs - ): - super().__init__(connection, **kwargs) - self.checkfirst = checkfirst - self.tables = tables - self.preparer = dialect.identifier_preparer - self.dialect = dialect - self.memo = {} - - def _can_create_table(self, table): - self.dialect.validate_identifier(table.name) - effective_schema = self.connection.schema_for_object(table) - if effective_schema: - self.dialect.validate_identifier(effective_schema) - return not self.checkfirst or not self.dialect.has_table( - self.connection, table.name, schema=effective_schema - ) - - def _can_create_index(self, index): - effective_schema = self.connection.schema_for_object(index.table) - if effective_schema: - self.dialect.validate_identifier(effective_schema) - return not self.checkfirst or not self.dialect.has_index( - self.connection, - index.table.name, - index.name, - schema=effective_schema, - ) - - def _can_create_sequence(self, sequence): - effective_schema = self.connection.schema_for_object(sequence) - - return self.dialect.supports_sequences and ( - (not self.dialect.sequences_optional or not sequence.optional) - and ( - not self.checkfirst - or not self.dialect.has_sequence( - self.connection, sequence.name, schema=effective_schema - ) - ) - ) - - def visit_metadata(self, metadata): - if self.tables is not None: - tables = self.tables - else: - tables = list(metadata.tables.values()) - - collection = sort_tables_and_constraints( - [t for t in tables if self._can_create_table(t)] - ) - - seq_coll = [ - s - for s in metadata._sequences.values() - if s.column is None and self._can_create_sequence(s) - ] - - event_collection = [t for (t, fks) in collection if t is not None] - - with self.with_ddl_events( - metadata, - tables=event_collection, - checkfirst=self.checkfirst, - ): - for seq in seq_coll: - self.traverse_single(seq, create_ok=True) - - for table, fkcs in collection: - if table is not None: - self.traverse_single( - table, - create_ok=True, - include_foreign_key_constraints=fkcs, - _is_metadata_operation=True, - ) - else: - for fkc in fkcs: - self.traverse_single(fkc) - - def visit_table( - self, - table, - create_ok=False, - include_foreign_key_constraints=None, - _is_metadata_operation=False, - ): - if not create_ok and not self._can_create_table(table): - return - - with self.with_ddl_events( - table, - checkfirst=self.checkfirst, - _is_metadata_operation=_is_metadata_operation, - ): - for column in table.columns: - if column.default is not None: - self.traverse_single(column.default) - - if not self.dialect.supports_alter: - # e.g., don't omit any foreign key constraints - include_foreign_key_constraints = None - - CreateTable( - table, - include_foreign_key_constraints=( - include_foreign_key_constraints - ), - )._invoke_with(self.connection) - - if hasattr(table, "indexes"): - for index in table.indexes: - self.traverse_single(index, create_ok=True) - - if ( - self.dialect.supports_comments - and not self.dialect.inline_comments - ): - if table.comment is not None: - SetTableComment(table)._invoke_with(self.connection) - - for column in table.columns: - if column.comment is not None: - SetColumnComment(column)._invoke_with(self.connection) - - if self.dialect.supports_constraint_comments: - for constraint in table.constraints: - if constraint.comment is not None: - self.connection.execute( - SetConstraintComment(constraint) - ) - - def visit_foreign_key_constraint(self, constraint): - if not self.dialect.supports_alter: - return - - with self.with_ddl_events(constraint): - AddConstraint(constraint)._invoke_with(self.connection) - - def visit_sequence(self, sequence, create_ok=False): - if not create_ok and not self._can_create_sequence(sequence): - return - with self.with_ddl_events(sequence): - CreateSequence(sequence)._invoke_with(self.connection) - - def visit_index(self, index, create_ok=False): - if not create_ok and not self._can_create_index(index): - return - with self.with_ddl_events(index): - CreateIndex(index)._invoke_with(self.connection) - - -class SchemaDropper(InvokeDropDDLBase): - def __init__( - self, dialect, connection, checkfirst=False, tables=None, **kwargs - ): - super().__init__(connection, **kwargs) - self.checkfirst = checkfirst - self.tables = tables - self.preparer = dialect.identifier_preparer - self.dialect = dialect - self.memo = {} - - def visit_metadata(self, metadata): - if self.tables is not None: - tables = self.tables - else: - tables = list(metadata.tables.values()) - - try: - unsorted_tables = [t for t in tables if self._can_drop_table(t)] - collection = list( - reversed( - sort_tables_and_constraints( - unsorted_tables, - filter_fn=lambda constraint: ( - False - if not self.dialect.supports_alter - or constraint.name is None - else None - ), - ) - ) - ) - except exc.CircularDependencyError as err2: - if not self.dialect.supports_alter: - util.warn( - "Can't sort tables for DROP; an " - "unresolvable foreign key " - "dependency exists between tables: %s; and backend does " - "not support ALTER. To restore at least a partial sort, " - "apply use_alter=True to ForeignKey and " - "ForeignKeyConstraint " - "objects involved in the cycle to mark these as known " - "cycles that will be ignored." - % (", ".join(sorted([t.fullname for t in err2.cycles]))) - ) - collection = [(t, ()) for t in unsorted_tables] - else: - raise exc.CircularDependencyError( - err2.args[0], - err2.cycles, - err2.edges, - msg="Can't sort tables for DROP; an " - "unresolvable foreign key " - "dependency exists between tables: %s. Please ensure " - "that the ForeignKey and ForeignKeyConstraint objects " - "involved in the cycle have " - "names so that they can be dropped using " - "DROP CONSTRAINT." - % (", ".join(sorted([t.fullname for t in err2.cycles]))), - ) from err2 - - seq_coll = [ - s - for s in metadata._sequences.values() - if self._can_drop_sequence(s) - ] - - event_collection = [t for (t, fks) in collection if t is not None] - - with self.with_ddl_events( - metadata, - tables=event_collection, - checkfirst=self.checkfirst, - ): - for table, fkcs in collection: - if table is not None: - self.traverse_single( - table, - drop_ok=True, - _is_metadata_operation=True, - _ignore_sequences=seq_coll, - ) - else: - for fkc in fkcs: - self.traverse_single(fkc) - - for seq in seq_coll: - self.traverse_single(seq, drop_ok=seq.column is None) - - def _can_drop_table(self, table): - self.dialect.validate_identifier(table.name) - effective_schema = self.connection.schema_for_object(table) - if effective_schema: - self.dialect.validate_identifier(effective_schema) - return not self.checkfirst or self.dialect.has_table( - self.connection, table.name, schema=effective_schema - ) - - def _can_drop_index(self, index): - effective_schema = self.connection.schema_for_object(index.table) - if effective_schema: - self.dialect.validate_identifier(effective_schema) - return not self.checkfirst or self.dialect.has_index( - self.connection, - index.table.name, - index.name, - schema=effective_schema, - ) - - def _can_drop_sequence(self, sequence): - effective_schema = self.connection.schema_for_object(sequence) - return self.dialect.supports_sequences and ( - (not self.dialect.sequences_optional or not sequence.optional) - and ( - not self.checkfirst - or self.dialect.has_sequence( - self.connection, sequence.name, schema=effective_schema - ) - ) - ) - - def visit_index(self, index, drop_ok=False): - if not drop_ok and not self._can_drop_index(index): - return - - with self.with_ddl_events(index): - DropIndex(index)(index, self.connection) - - def visit_table( - self, - table, - drop_ok=False, - _is_metadata_operation=False, - _ignore_sequences=(), - ): - if not drop_ok and not self._can_drop_table(table): - return - - with self.with_ddl_events( - table, - checkfirst=self.checkfirst, - _is_metadata_operation=_is_metadata_operation, - ): - DropTable(table)._invoke_with(self.connection) - - # traverse client side defaults which may refer to server-side - # sequences. noting that some of these client side defaults may - # also be set up as server side defaults - # (see https://docs.sqlalchemy.org/en/ - # latest/core/defaults.html - # #associating-a-sequence-as-the-server-side- - # default), so have to be dropped after the table is dropped. - for column in table.columns: - if ( - column.default is not None - and column.default not in _ignore_sequences - ): - self.traverse_single(column.default) - - def visit_foreign_key_constraint(self, constraint): - if not self.dialect.supports_alter: - return - with self.with_ddl_events(constraint): - DropConstraint(constraint)._invoke_with(self.connection) - - def visit_sequence(self, sequence, drop_ok=False): - if not drop_ok and not self._can_drop_sequence(sequence): - return - with self.with_ddl_events(sequence): - DropSequence(sequence)._invoke_with(self.connection) - - -def sort_tables( - tables: Iterable[TableClause], - skip_fn: Optional[Callable[[ForeignKeyConstraint], bool]] = None, - extra_dependencies: Optional[ - typing_Sequence[Tuple[TableClause, TableClause]] - ] = None, -) -> List[Table]: - """Sort a collection of :class:`_schema.Table` objects based on - dependency. - - This is a dependency-ordered sort which will emit :class:`_schema.Table` - objects such that they will follow their dependent :class:`_schema.Table` - objects. - Tables are dependent on another based on the presence of - :class:`_schema.ForeignKeyConstraint` - objects as well as explicit dependencies - added by :meth:`_schema.Table.add_is_dependent_on`. - - .. warning:: - - The :func:`._schema.sort_tables` function cannot by itself - accommodate automatic resolution of dependency cycles between - tables, which are usually caused by mutually dependent foreign key - constraints. When these cycles are detected, the foreign keys - of these tables are omitted from consideration in the sort. - A warning is emitted when this condition occurs, which will be an - exception raise in a future release. Tables which are not part - of the cycle will still be returned in dependency order. - - To resolve these cycles, the - :paramref:`_schema.ForeignKeyConstraint.use_alter` parameter may be - applied to those constraints which create a cycle. Alternatively, - the :func:`_schema.sort_tables_and_constraints` function will - automatically return foreign key constraints in a separate - collection when cycles are detected so that they may be applied - to a schema separately. - - .. versionchanged:: 1.3.17 - a warning is emitted when - :func:`_schema.sort_tables` cannot perform a proper sort due to - cyclical dependencies. This will be an exception in a future - release. Additionally, the sort will continue to return - other tables not involved in the cycle in dependency order - which was not the case previously. - - :param tables: a sequence of :class:`_schema.Table` objects. - - :param skip_fn: optional callable which will be passed a - :class:`_schema.ForeignKeyConstraint` object; if it returns True, this - constraint will not be considered as a dependency. Note this is - **different** from the same parameter in - :func:`.sort_tables_and_constraints`, which is - instead passed the owning :class:`_schema.ForeignKeyConstraint` object. - - :param extra_dependencies: a sequence of 2-tuples of tables which will - also be considered as dependent on each other. - - .. seealso:: - - :func:`.sort_tables_and_constraints` - - :attr:`_schema.MetaData.sorted_tables` - uses this function to sort - - - """ - - if skip_fn is not None: - fixed_skip_fn = skip_fn - - def _skip_fn(fkc): - for fk in fkc.elements: - if fixed_skip_fn(fk): - return True - else: - return None - - else: - _skip_fn = None # type: ignore - - return [ - t - for (t, fkcs) in sort_tables_and_constraints( - tables, - filter_fn=_skip_fn, - extra_dependencies=extra_dependencies, - _warn_for_cycles=True, - ) - if t is not None - ] - - -def sort_tables_and_constraints( - tables, filter_fn=None, extra_dependencies=None, _warn_for_cycles=False -): - """Sort a collection of :class:`_schema.Table` / - :class:`_schema.ForeignKeyConstraint` - objects. - - This is a dependency-ordered sort which will emit tuples of - ``(Table, [ForeignKeyConstraint, ...])`` such that each - :class:`_schema.Table` follows its dependent :class:`_schema.Table` - objects. - Remaining :class:`_schema.ForeignKeyConstraint` - objects that are separate due to - dependency rules not satisfied by the sort are emitted afterwards - as ``(None, [ForeignKeyConstraint ...])``. - - Tables are dependent on another based on the presence of - :class:`_schema.ForeignKeyConstraint` objects, explicit dependencies - added by :meth:`_schema.Table.add_is_dependent_on`, - as well as dependencies - stated here using the :paramref:`~.sort_tables_and_constraints.skip_fn` - and/or :paramref:`~.sort_tables_and_constraints.extra_dependencies` - parameters. - - :param tables: a sequence of :class:`_schema.Table` objects. - - :param filter_fn: optional callable which will be passed a - :class:`_schema.ForeignKeyConstraint` object, - and returns a value based on - whether this constraint should definitely be included or excluded as - an inline constraint, or neither. If it returns False, the constraint - will definitely be included as a dependency that cannot be subject - to ALTER; if True, it will **only** be included as an ALTER result at - the end. Returning None means the constraint is included in the - table-based result unless it is detected as part of a dependency cycle. - - :param extra_dependencies: a sequence of 2-tuples of tables which will - also be considered as dependent on each other. - - .. seealso:: - - :func:`.sort_tables` - - - """ - - fixed_dependencies = set() - mutable_dependencies = set() - - if extra_dependencies is not None: - fixed_dependencies.update(extra_dependencies) - - remaining_fkcs = set() - for table in tables: - for fkc in table.foreign_key_constraints: - if fkc.use_alter is True: - remaining_fkcs.add(fkc) - continue - - if filter_fn: - filtered = filter_fn(fkc) - - if filtered is True: - remaining_fkcs.add(fkc) - continue - - dependent_on = fkc.referred_table - if dependent_on is not table: - mutable_dependencies.add((dependent_on, table)) - - fixed_dependencies.update( - (parent, table) for parent in table._extra_dependencies - ) - - try: - candidate_sort = list( - topological.sort( - fixed_dependencies.union(mutable_dependencies), - tables, - ) - ) - except exc.CircularDependencyError as err: - if _warn_for_cycles: - util.warn( - "Cannot correctly sort tables; there are unresolvable cycles " - 'between tables "%s", which is usually caused by mutually ' - "dependent foreign key constraints. Foreign key constraints " - "involving these tables will not be considered; this warning " - "may raise an error in a future release." - % (", ".join(sorted(t.fullname for t in err.cycles)),) - ) - for edge in err.edges: - if edge in mutable_dependencies: - table = edge[1] - if table not in err.cycles: - continue - can_remove = [ - fkc - for fkc in table.foreign_key_constraints - if filter_fn is None or filter_fn(fkc) is not False - ] - remaining_fkcs.update(can_remove) - for fkc in can_remove: - dependent_on = fkc.referred_table - if dependent_on is not table: - mutable_dependencies.discard((dependent_on, table)) - candidate_sort = list( - topological.sort( - fixed_dependencies.union(mutable_dependencies), - tables, - ) - ) - - return [ - (table, table.foreign_key_constraints.difference(remaining_fkcs)) - for table in candidate_sort - ] + [(None, list(remaining_fkcs))] diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/default_comparator.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/default_comparator.py deleted file mode 100644 index 76131bc..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/default_comparator.py +++ /dev/null @@ -1,552 +0,0 @@ -# sql/default_comparator.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""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), -} diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/dml.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/dml.py deleted file mode 100644 index 779be1d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/dml.py +++ /dev/null @@ -1,1817 +0,0 @@ -# sql/dml.py -# Copyright (C) 2009-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -""" -Provide :class:`_expression.Insert`, :class:`_expression.Update` and -:class:`_expression.Delete`. - -""" -from __future__ import annotations - -import collections.abc as collections_abc -import operator -from typing import Any -from typing import cast -from typing import Dict -from typing import Iterable -from typing import List -from typing import MutableMapping -from typing import NoReturn -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 typing import Union - -from . import coercions -from . import roles -from . import util as sql_util -from ._typing import _TP -from ._typing import _unexpected_kw -from ._typing import is_column_element -from ._typing import is_named_from_clause -from .base import _entity_namespace_key -from .base import _exclusive_against -from .base import _from_objects -from .base import _generative -from .base import _select_iterables -from .base import ColumnCollection -from .base import CompileState -from .base import DialectKWArgs -from .base import Executable -from .base import Generative -from .base import HasCompileState -from .elements import BooleanClauseList -from .elements import ClauseElement -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import Null -from .selectable import Alias -from .selectable import ExecutableReturnsRows -from .selectable import FromClause -from .selectable import HasCTE -from .selectable import HasPrefixes -from .selectable import Join -from .selectable import SelectLabelStyle -from .selectable import TableClause -from .selectable import TypedReturnsRows -from .sqltypes import NullType -from .visitors import InternalTraversal -from .. import exc -from .. import util -from ..util.typing import Self -from ..util.typing import TypeGuard - -if TYPE_CHECKING: - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnsClauseArgument - from ._typing import _DMLColumnArgument - from ._typing import _DMLColumnKeyMapping - from ._typing import _DMLTableArgument - from ._typing import _T0 # noqa - from ._typing import _T1 # noqa - from ._typing import _T2 # noqa - from ._typing import _T3 # noqa - from ._typing import _T4 # noqa - from ._typing import _T5 # noqa - from ._typing import _T6 # noqa - from ._typing import _T7 # noqa - from ._typing import _TypedColumnClauseArgument as _TCCA # noqa - from .base import ReadOnlyColumnCollection - from .compiler import SQLCompiler - from .elements import KeyedColumnElement - from .selectable import _ColumnsClauseElement - from .selectable import _SelectIterable - from .selectable import Select - from .selectable import Selectable - - def isupdate(dml: DMLState) -> TypeGuard[UpdateDMLState]: ... - - def isdelete(dml: DMLState) -> TypeGuard[DeleteDMLState]: ... - - def isinsert(dml: DMLState) -> TypeGuard[InsertDMLState]: ... - -else: - isupdate = operator.attrgetter("isupdate") - isdelete = operator.attrgetter("isdelete") - isinsert = operator.attrgetter("isinsert") - - -_T = TypeVar("_T", bound=Any) - -_DMLColumnElement = Union[str, ColumnClause[Any]] -_DMLTableElement = Union[TableClause, Alias, Join] - - -class DMLState(CompileState): - _no_parameters = True - _dict_parameters: Optional[MutableMapping[_DMLColumnElement, Any]] = None - _multi_parameters: Optional[ - List[MutableMapping[_DMLColumnElement, Any]] - ] = None - _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None - _parameter_ordering: Optional[List[_DMLColumnElement]] = None - _primary_table: FromClause - _supports_implicit_returning = True - - isupdate = False - isdelete = False - isinsert = False - - statement: UpdateBase - - def __init__( - self, statement: UpdateBase, compiler: SQLCompiler, **kw: Any - ): - raise NotImplementedError() - - @classmethod - def get_entity_description(cls, statement: UpdateBase) -> Dict[str, Any]: - return { - "name": ( - statement.table.name - if is_named_from_clause(statement.table) - else None - ), - "table": statement.table, - } - - @classmethod - def get_returning_column_descriptions( - cls, statement: UpdateBase - ) -> List[Dict[str, Any]]: - return [ - { - "name": c.key, - "type": c.type, - "expr": c, - } - for c in statement._all_selected_columns - ] - - @property - def dml_table(self) -> _DMLTableElement: - return self.statement.table - - if TYPE_CHECKING: - - @classmethod - def get_plugin_class(cls, statement: Executable) -> Type[DMLState]: ... - - @classmethod - def _get_multi_crud_kv_pairs( - cls, - statement: UpdateBase, - multi_kv_iterator: Iterable[Dict[_DMLColumnArgument, Any]], - ) -> List[Dict[_DMLColumnElement, Any]]: - return [ - { - coercions.expect(roles.DMLColumnRole, k): v - for k, v in mapping.items() - } - for mapping in multi_kv_iterator - ] - - @classmethod - def _get_crud_kv_pairs( - cls, - statement: UpdateBase, - kv_iterator: Iterable[Tuple[_DMLColumnArgument, Any]], - needs_to_be_cacheable: bool, - ) -> List[Tuple[_DMLColumnElement, Any]]: - return [ - ( - coercions.expect(roles.DMLColumnRole, k), - ( - v - if not needs_to_be_cacheable - else coercions.expect( - roles.ExpressionElementRole, - v, - type_=NullType(), - is_crud=True, - ) - ), - ) - for k, v in kv_iterator - ] - - def _make_extra_froms( - self, statement: DMLWhereBase - ) -> Tuple[FromClause, List[FromClause]]: - froms: List[FromClause] = [] - - all_tables = list(sql_util.tables_from_leftmost(statement.table)) - primary_table = all_tables[0] - seen = {primary_table} - - consider = statement._where_criteria - if self._dict_parameters: - consider += tuple(self._dict_parameters.values()) - - for crit in consider: - for item in _from_objects(crit): - if not seen.intersection(item._cloned_set): - froms.append(item) - seen.update(item._cloned_set) - - froms.extend(all_tables[1:]) - return primary_table, froms - - def _process_values(self, statement: ValuesBase) -> None: - if self._no_parameters: - self._dict_parameters = statement._values - self._no_parameters = False - - def _process_select_values(self, statement: ValuesBase) -> None: - assert statement._select_names is not None - parameters: MutableMapping[_DMLColumnElement, Any] = { - name: Null() for name in statement._select_names - } - - if self._no_parameters: - self._no_parameters = False - self._dict_parameters = parameters - else: - # this condition normally not reachable as the Insert - # does not allow this construction to occur - assert False, "This statement already has parameters" - - def _no_multi_values_supported(self, statement: ValuesBase) -> NoReturn: - raise exc.InvalidRequestError( - "%s construct does not support " - "multiple parameter sets." % statement.__visit_name__.upper() - ) - - def _cant_mix_formats_error(self) -> NoReturn: - raise exc.InvalidRequestError( - "Can't mix single and multiple VALUES " - "formats in one INSERT statement; one style appends to a " - "list while the other replaces values, so the intent is " - "ambiguous." - ) - - -@CompileState.plugin_for("default", "insert") -class InsertDMLState(DMLState): - isinsert = True - - include_table_with_column_exprs = False - - _has_multi_parameters = False - - def __init__( - self, - statement: Insert, - compiler: SQLCompiler, - disable_implicit_returning: bool = False, - **kw: Any, - ): - self.statement = statement - self._primary_table = statement.table - - if disable_implicit_returning: - self._supports_implicit_returning = False - - self.isinsert = True - if statement._select_names: - self._process_select_values(statement) - if statement._values is not None: - self._process_values(statement) - if statement._multi_values: - self._process_multi_values(statement) - - @util.memoized_property - def _insert_col_keys(self) -> List[str]: - # this is also done in crud.py -> _key_getters_for_crud_column - return [ - coercions.expect(roles.DMLColumnRole, col, as_key=True) - for col in self._dict_parameters or () - ] - - def _process_values(self, statement: ValuesBase) -> None: - if self._no_parameters: - self._has_multi_parameters = False - self._dict_parameters = statement._values - self._no_parameters = False - elif self._has_multi_parameters: - self._cant_mix_formats_error() - - def _process_multi_values(self, statement: ValuesBase) -> None: - for parameters in statement._multi_values: - multi_parameters: List[MutableMapping[_DMLColumnElement, Any]] = [ - ( - { - c.key: value - for c, value in zip(statement.table.c, parameter_set) - } - if isinstance(parameter_set, collections_abc.Sequence) - else parameter_set - ) - for parameter_set in parameters - ] - - if self._no_parameters: - self._no_parameters = False - self._has_multi_parameters = True - self._multi_parameters = multi_parameters - self._dict_parameters = self._multi_parameters[0] - elif not self._has_multi_parameters: - self._cant_mix_formats_error() - else: - assert self._multi_parameters - self._multi_parameters.extend(multi_parameters) - - -@CompileState.plugin_for("default", "update") -class UpdateDMLState(DMLState): - isupdate = True - - include_table_with_column_exprs = False - - def __init__(self, statement: Update, compiler: SQLCompiler, **kw: Any): - self.statement = statement - - self.isupdate = True - if statement._ordered_values is not None: - self._process_ordered_values(statement) - elif statement._values is not None: - self._process_values(statement) - elif statement._multi_values: - self._no_multi_values_supported(statement) - t, ef = self._make_extra_froms(statement) - self._primary_table = t - self._extra_froms = ef - - self.is_multitable = mt = ef - self.include_table_with_column_exprs = bool( - mt and compiler.render_table_with_column_in_update_from - ) - - def _process_ordered_values(self, statement: ValuesBase) -> None: - parameters = statement._ordered_values - - if self._no_parameters: - self._no_parameters = False - assert parameters is not None - self._dict_parameters = dict(parameters) - self._ordered_values = parameters - self._parameter_ordering = [key for key, value in parameters] - else: - raise exc.InvalidRequestError( - "Can only invoke ordered_values() once, and not mixed " - "with any other values() call" - ) - - -@CompileState.plugin_for("default", "delete") -class DeleteDMLState(DMLState): - isdelete = True - - def __init__(self, statement: Delete, compiler: SQLCompiler, **kw: Any): - self.statement = statement - - self.isdelete = True - t, ef = self._make_extra_froms(statement) - self._primary_table = t - self._extra_froms = ef - self.is_multitable = ef - - -class UpdateBase( - roles.DMLRole, - HasCTE, - HasCompileState, - DialectKWArgs, - HasPrefixes, - Generative, - ExecutableReturnsRows, - ClauseElement, -): - """Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements.""" - - __visit_name__ = "update_base" - - _hints: util.immutabledict[Tuple[_DMLTableElement, str], str] = ( - util.EMPTY_DICT - ) - named_with_column = False - - _label_style: SelectLabelStyle = ( - SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY - ) - table: _DMLTableElement - - _return_defaults = False - _return_defaults_columns: Optional[Tuple[_ColumnsClauseElement, ...]] = ( - None - ) - _supplemental_returning: Optional[Tuple[_ColumnsClauseElement, ...]] = None - _returning: Tuple[_ColumnsClauseElement, ...] = () - - is_dml = True - - def _generate_fromclause_column_proxies( - self, fromclause: FromClause - ) -> None: - fromclause._columns._populate_separate_keys( - col._make_proxy(fromclause) - for col in self._all_selected_columns - if is_column_element(col) - ) - - def params(self, *arg: Any, **kw: Any) -> NoReturn: - """Set the parameters for the statement. - - This method raises ``NotImplementedError`` on the base class, - and is overridden by :class:`.ValuesBase` to provide the - SET/VALUES clause of UPDATE and INSERT. - - """ - raise NotImplementedError( - "params() is not supported for INSERT/UPDATE/DELETE statements." - " To set the values for an INSERT or UPDATE statement, use" - " stmt.values(**parameters)." - ) - - @_generative - def with_dialect_options(self, **opt: Any) -> Self: - """Add dialect options to this INSERT/UPDATE/DELETE object. - - e.g.:: - - upd = table.update().dialect_options(mysql_limit=10) - - .. versionadded: 1.4 - this method supersedes the dialect options - associated with the constructor. - - - """ - self._validate_dialect_kwargs(opt) - return self - - @_generative - def return_defaults( - self, - *cols: _DMLColumnArgument, - supplemental_cols: Optional[Iterable[_DMLColumnArgument]] = None, - sort_by_parameter_order: bool = False, - ) -> Self: - """Make use of a :term:`RETURNING` clause for the purpose - of fetching server-side expressions and defaults, for supporting - backends only. - - .. deepalchemy:: - - The :meth:`.UpdateBase.return_defaults` method is used by the ORM - for its internal work in fetching newly generated primary key - and server default values, in particular to provide the underyling - implementation of the :paramref:`_orm.Mapper.eager_defaults` - ORM feature as well as to allow RETURNING support with bulk - ORM inserts. Its behavior is fairly idiosyncratic - and is not really intended for general use. End users should - stick with using :meth:`.UpdateBase.returning` in order to - add RETURNING clauses to their INSERT, UPDATE and DELETE - statements. - - Normally, a single row INSERT statement will automatically populate the - :attr:`.CursorResult.inserted_primary_key` attribute when executed, - which stores the primary key of the row that was just inserted in the - form of a :class:`.Row` object with column names as named tuple keys - (and the :attr:`.Row._mapping` view fully populated as well). The - dialect in use chooses the strategy to use in order to populate this - data; if it was generated using server-side defaults and / or SQL - expressions, dialect-specific approaches such as ``cursor.lastrowid`` - or ``RETURNING`` are typically used to acquire the new primary key - value. - - However, when the statement is modified by calling - :meth:`.UpdateBase.return_defaults` before executing the statement, - additional behaviors take place **only** for backends that support - RETURNING and for :class:`.Table` objects that maintain the - :paramref:`.Table.implicit_returning` parameter at its default value of - ``True``. In these cases, when the :class:`.CursorResult` is returned - from the statement's execution, not only will - :attr:`.CursorResult.inserted_primary_key` be populated as always, the - :attr:`.CursorResult.returned_defaults` attribute will also be - populated with a :class:`.Row` named-tuple representing the full range - of server generated - values from that single row, including values for any columns that - specify :paramref:`_schema.Column.server_default` or which make use of - :paramref:`_schema.Column.default` using a SQL expression. - - When invoking INSERT statements with multiple rows using - :ref:`insertmanyvalues <engine_insertmanyvalues>`, the - :meth:`.UpdateBase.return_defaults` modifier will have the effect of - the :attr:`_engine.CursorResult.inserted_primary_key_rows` and - :attr:`_engine.CursorResult.returned_defaults_rows` attributes being - fully populated with lists of :class:`.Row` objects representing newly - inserted primary key values as well as newly inserted server generated - values for each row inserted. The - :attr:`.CursorResult.inserted_primary_key` and - :attr:`.CursorResult.returned_defaults` attributes will also continue - to be populated with the first row of these two collections. - - If the backend does not support RETURNING or the :class:`.Table` in use - has disabled :paramref:`.Table.implicit_returning`, then no RETURNING - clause is added and no additional data is fetched, however the - INSERT, UPDATE or DELETE statement proceeds normally. - - E.g.:: - - stmt = table.insert().values(data='newdata').return_defaults() - - result = connection.execute(stmt) - - server_created_at = result.returned_defaults['created_at'] - - When used against an UPDATE statement - :meth:`.UpdateBase.return_defaults` instead looks for columns that - include :paramref:`_schema.Column.onupdate` or - :paramref:`_schema.Column.server_onupdate` parameters assigned, when - constructing the columns that will be included in the RETURNING clause - by default if explicit columns were not specified. When used against a - DELETE statement, no columns are included in RETURNING by default, they - instead must be specified explicitly as there are no columns that - normally change values when a DELETE statement proceeds. - - .. versionadded:: 2.0 :meth:`.UpdateBase.return_defaults` is supported - for DELETE statements also and has been moved from - :class:`.ValuesBase` to :class:`.UpdateBase`. - - The :meth:`.UpdateBase.return_defaults` method is mutually exclusive - against the :meth:`.UpdateBase.returning` method and errors will be - raised during the SQL compilation process if both are used at the same - time on one statement. The RETURNING clause of the INSERT, UPDATE or - DELETE statement is therefore controlled by only one of these methods - at a time. - - The :meth:`.UpdateBase.return_defaults` method differs from - :meth:`.UpdateBase.returning` in these ways: - - 1. :meth:`.UpdateBase.return_defaults` method causes the - :attr:`.CursorResult.returned_defaults` collection to be populated - with the first row from the RETURNING result. This attribute is not - populated when using :meth:`.UpdateBase.returning`. - - 2. :meth:`.UpdateBase.return_defaults` is compatible with existing - logic used to fetch auto-generated primary key values that are then - populated into the :attr:`.CursorResult.inserted_primary_key` - attribute. By contrast, using :meth:`.UpdateBase.returning` will - have the effect of the :attr:`.CursorResult.inserted_primary_key` - attribute being left unpopulated. - - 3. :meth:`.UpdateBase.return_defaults` can be called against any - backend. Backends that don't support RETURNING will skip the usage - of the feature, rather than raising an exception, *unless* - ``supplemental_cols`` is passed. The return value - of :attr:`_engine.CursorResult.returned_defaults` will be ``None`` - for backends that don't support RETURNING or for which the target - :class:`.Table` sets :paramref:`.Table.implicit_returning` to - ``False``. - - 4. An INSERT statement invoked with executemany() is supported if the - backend database driver supports the - :ref:`insertmanyvalues <engine_insertmanyvalues>` - feature which is now supported by most SQLAlchemy-included backends. - When executemany is used, the - :attr:`_engine.CursorResult.returned_defaults_rows` and - :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors - will return the inserted defaults and primary keys. - - .. versionadded:: 1.4 Added - :attr:`_engine.CursorResult.returned_defaults_rows` and - :attr:`_engine.CursorResult.inserted_primary_key_rows` accessors. - In version 2.0, the underlying implementation which fetches and - populates the data for these attributes was generalized to be - supported by most backends, whereas in 1.4 they were only - supported by the ``psycopg2`` driver. - - - :param cols: optional list of column key names or - :class:`_schema.Column` that acts as a filter for those columns that - will be fetched. - :param supplemental_cols: optional list of RETURNING expressions, - in the same form as one would pass to the - :meth:`.UpdateBase.returning` method. When present, the additional - columns will be included in the RETURNING clause, and the - :class:`.CursorResult` object will be "rewound" when returned, so - that methods like :meth:`.CursorResult.all` will return new rows - mostly as though the statement used :meth:`.UpdateBase.returning` - directly. However, unlike when using :meth:`.UpdateBase.returning` - directly, the **order of the columns is undefined**, so can only be - targeted using names or :attr:`.Row._mapping` keys; they cannot - reliably be targeted positionally. - - .. versionadded:: 2.0 - - :param sort_by_parameter_order: for a batch INSERT that is being - executed against multiple parameter sets, organize the results of - RETURNING so that the returned rows correspond to the order of - parameter sets passed in. This applies only to an :term:`executemany` - execution for supporting dialects and typically makes use of the - :term:`insertmanyvalues` feature. - - .. versionadded:: 2.0.10 - - .. seealso:: - - :ref:`engine_insertmanyvalues_returning_order` - background on - sorting of RETURNING rows for bulk INSERT - - .. seealso:: - - :meth:`.UpdateBase.returning` - - :attr:`_engine.CursorResult.returned_defaults` - - :attr:`_engine.CursorResult.returned_defaults_rows` - - :attr:`_engine.CursorResult.inserted_primary_key` - - :attr:`_engine.CursorResult.inserted_primary_key_rows` - - """ - - if self._return_defaults: - # note _return_defaults_columns = () means return all columns, - # so if we have been here before, only update collection if there - # are columns in the collection - if self._return_defaults_columns and cols: - self._return_defaults_columns = tuple( - util.OrderedSet(self._return_defaults_columns).union( - coercions.expect(roles.ColumnsClauseRole, c) - for c in cols - ) - ) - else: - # set for all columns - self._return_defaults_columns = () - else: - self._return_defaults_columns = tuple( - coercions.expect(roles.ColumnsClauseRole, c) for c in cols - ) - self._return_defaults = True - if sort_by_parameter_order: - if not self.is_insert: - raise exc.ArgumentError( - "The 'sort_by_parameter_order' argument to " - "return_defaults() only applies to INSERT statements" - ) - self._sort_by_parameter_order = True - if supplemental_cols: - # uniquifying while also maintaining order (the maintain of order - # is for test suites but also for vertical splicing - supplemental_col_tup = ( - coercions.expect(roles.ColumnsClauseRole, c) - for c in supplemental_cols - ) - - if self._supplemental_returning is None: - self._supplemental_returning = tuple( - util.unique_list(supplemental_col_tup) - ) - else: - self._supplemental_returning = tuple( - util.unique_list( - self._supplemental_returning - + tuple(supplemental_col_tup) - ) - ) - - return self - - @_generative - def returning( - self, - *cols: _ColumnsClauseArgument[Any], - sort_by_parameter_order: bool = False, - **__kw: Any, - ) -> UpdateBase: - r"""Add a :term:`RETURNING` or equivalent clause to this statement. - - e.g.: - - .. sourcecode:: pycon+sql - - >>> stmt = ( - ... table.update() - ... .where(table.c.data == "value") - ... .values(status="X") - ... .returning(table.c.server_flag, table.c.updated_timestamp) - ... ) - >>> print(stmt) - {printsql}UPDATE some_table SET status=:status - WHERE some_table.data = :data_1 - RETURNING some_table.server_flag, some_table.updated_timestamp - - The method may be invoked multiple times to add new entries to the - list of expressions to be returned. - - .. versionadded:: 1.4.0b2 The method may be invoked multiple times to - add new entries to the list of expressions to be returned. - - The given collection of column expressions should be derived from the - table that is the target of the INSERT, UPDATE, or DELETE. While - :class:`_schema.Column` objects are typical, the elements can also be - expressions: - - .. sourcecode:: pycon+sql - - >>> stmt = table.insert().returning( - ... (table.c.first_name + " " + table.c.last_name).label("fullname") - ... ) - >>> print(stmt) - {printsql}INSERT INTO some_table (first_name, last_name) - VALUES (:first_name, :last_name) - RETURNING some_table.first_name || :first_name_1 || some_table.last_name AS fullname - - Upon compilation, a RETURNING clause, or database equivalent, - will be rendered within the statement. For INSERT and UPDATE, - the values are the newly inserted/updated values. For DELETE, - the values are those of the rows which were deleted. - - Upon execution, the values of the columns to be returned are made - available via the result set and can be iterated using - :meth:`_engine.CursorResult.fetchone` and similar. - For DBAPIs which do not - natively support returning values (i.e. cx_oracle), SQLAlchemy will - approximate this behavior at the result level so that a reasonable - amount of behavioral neutrality is provided. - - Note that not all databases/DBAPIs - support RETURNING. For those backends with no support, - an exception is raised upon compilation and/or execution. - For those who do support it, the functionality across backends - varies greatly, including restrictions on executemany() - and other statements which return multiple rows. Please - read the documentation notes for the database in use in - order to determine the availability of RETURNING. - - :param \*cols: series of columns, SQL expressions, or whole tables - entities to be returned. - :param sort_by_parameter_order: for a batch INSERT that is being - executed against multiple parameter sets, organize the results of - RETURNING so that the returned rows correspond to the order of - parameter sets passed in. This applies only to an :term:`executemany` - execution for supporting dialects and typically makes use of the - :term:`insertmanyvalues` feature. - - .. versionadded:: 2.0.10 - - .. seealso:: - - :ref:`engine_insertmanyvalues_returning_order` - background on - sorting of RETURNING rows for bulk INSERT (Core level discussion) - - :ref:`orm_queryguide_bulk_insert_returning_ordered` - example of - use with :ref:`orm_queryguide_bulk_insert` (ORM level discussion) - - .. seealso:: - - :meth:`.UpdateBase.return_defaults` - an alternative method tailored - towards efficient fetching of server-side defaults and triggers - for single-row INSERTs or UPDATEs. - - :ref:`tutorial_insert_returning` - in the :ref:`unified_tutorial` - - """ # noqa: E501 - if __kw: - raise _unexpected_kw("UpdateBase.returning()", __kw) - if self._return_defaults: - raise exc.InvalidRequestError( - "return_defaults() is already configured on this statement" - ) - self._returning += tuple( - coercions.expect(roles.ColumnsClauseRole, c) for c in cols - ) - if sort_by_parameter_order: - if not self.is_insert: - raise exc.ArgumentError( - "The 'sort_by_parameter_order' argument to returning() " - "only applies to INSERT statements" - ) - self._sort_by_parameter_order = True - return self - - def corresponding_column( - self, column: KeyedColumnElement[Any], require_embedded: bool = False - ) -> Optional[ColumnElement[Any]]: - return self.exported_columns.corresponding_column( - column, require_embedded=require_embedded - ) - - @util.ro_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return [c for c in _select_iterables(self._returning)] - - @util.ro_memoized_property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[Optional[str], ColumnElement[Any]]: - """Return the RETURNING columns as a column collection for this - statement. - - .. versionadded:: 1.4 - - """ - return ColumnCollection( - (c.key, c) - for c in self._all_selected_columns - if is_column_element(c) - ).as_readonly() - - @_generative - def with_hint( - self, - text: str, - selectable: Optional[_DMLTableArgument] = None, - dialect_name: str = "*", - ) -> Self: - """Add a table hint for a single table to this - INSERT/UPDATE/DELETE statement. - - .. note:: - - :meth:`.UpdateBase.with_hint` currently applies only to - Microsoft SQL Server. For MySQL INSERT/UPDATE/DELETE hints, use - :meth:`.UpdateBase.prefix_with`. - - The text of the hint is rendered in the appropriate - location for the database backend in use, relative - to the :class:`_schema.Table` that is the subject of this - statement, or optionally to that of the given - :class:`_schema.Table` passed as the ``selectable`` argument. - - The ``dialect_name`` option will limit the rendering of a particular - hint to a particular backend. Such as, to add a hint - that only takes effect for SQL Server:: - - mytable.insert().with_hint("WITH (PAGLOCK)", dialect_name="mssql") - - :param text: Text of the hint. - :param selectable: optional :class:`_schema.Table` that specifies - an element of the FROM clause within an UPDATE or DELETE - to be the subject of the hint - applies only to certain backends. - :param dialect_name: defaults to ``*``, if specified as the name - of a particular dialect, will apply these hints only when - that dialect is in use. - """ - if selectable is None: - selectable = self.table - else: - selectable = coercions.expect(roles.DMLTableRole, selectable) - self._hints = self._hints.union({(selectable, dialect_name): text}) - return self - - @property - def entity_description(self) -> Dict[str, Any]: - """Return a :term:`plugin-enabled` description of the table and/or - entity which this DML construct is operating against. - - This attribute is generally useful when using the ORM, as an - extended structure which includes information about mapped - entities is returned. The section :ref:`queryguide_inspection` - contains more background. - - For a Core statement, the structure returned by this accessor - is derived from the :attr:`.UpdateBase.table` attribute, and - refers to the :class:`.Table` being inserted, updated, or deleted:: - - >>> stmt = insert(user_table) - >>> stmt.entity_description - { - "name": "user_table", - "table": Table("user_table", ...) - } - - .. versionadded:: 1.4.33 - - .. seealso:: - - :attr:`.UpdateBase.returning_column_descriptions` - - :attr:`.Select.column_descriptions` - entity information for - a :func:`.select` construct - - :ref:`queryguide_inspection` - ORM background - - """ - meth = DMLState.get_plugin_class(self).get_entity_description - return meth(self) - - @property - def returning_column_descriptions(self) -> List[Dict[str, Any]]: - """Return a :term:`plugin-enabled` description of the columns - which this DML construct is RETURNING against, in other words - the expressions established as part of :meth:`.UpdateBase.returning`. - - This attribute is generally useful when using the ORM, as an - extended structure which includes information about mapped - entities is returned. The section :ref:`queryguide_inspection` - contains more background. - - For a Core statement, the structure returned by this accessor is - derived from the same objects that are returned by the - :attr:`.UpdateBase.exported_columns` accessor:: - - >>> stmt = insert(user_table).returning(user_table.c.id, user_table.c.name) - >>> stmt.entity_description - [ - { - "name": "id", - "type": Integer, - "expr": Column("id", Integer(), table=<user>, ...) - }, - { - "name": "name", - "type": String(), - "expr": Column("name", String(), table=<user>, ...) - }, - ] - - .. versionadded:: 1.4.33 - - .. seealso:: - - :attr:`.UpdateBase.entity_description` - - :attr:`.Select.column_descriptions` - entity information for - a :func:`.select` construct - - :ref:`queryguide_inspection` - ORM background - - """ # noqa: E501 - meth = DMLState.get_plugin_class( - self - ).get_returning_column_descriptions - return meth(self) - - -class ValuesBase(UpdateBase): - """Supplies support for :meth:`.ValuesBase.values` to - INSERT and UPDATE constructs.""" - - __visit_name__ = "values_base" - - _supports_multi_parameters = False - - select: Optional[Select[Any]] = None - """SELECT statement for INSERT .. FROM SELECT""" - - _post_values_clause: Optional[ClauseElement] = None - """used by extensions to Insert etc. to add additional syntacitcal - constructs, e.g. ON CONFLICT etc.""" - - _values: Optional[util.immutabledict[_DMLColumnElement, Any]] = None - _multi_values: Tuple[ - Union[ - Sequence[Dict[_DMLColumnElement, Any]], - Sequence[Sequence[Any]], - ], - ..., - ] = () - - _ordered_values: Optional[List[Tuple[_DMLColumnElement, Any]]] = None - - _select_names: Optional[List[str]] = None - _inline: bool = False - - def __init__(self, table: _DMLTableArgument): - self.table = coercions.expect( - roles.DMLTableRole, table, apply_propagate_attrs=self - ) - - @_generative - @_exclusive_against( - "_select_names", - "_ordered_values", - msgs={ - "_select_names": "This construct already inserts from a SELECT", - "_ordered_values": "This statement already has ordered " - "values present", - }, - ) - def values( - self, - *args: Union[ - _DMLColumnKeyMapping[Any], - Sequence[Any], - ], - **kwargs: Any, - ) -> Self: - r"""Specify a fixed VALUES clause for an INSERT statement, or the SET - clause for an UPDATE. - - Note that the :class:`_expression.Insert` and - :class:`_expression.Update` - constructs support - per-execution time formatting of the VALUES and/or SET clauses, - based on the arguments passed to :meth:`_engine.Connection.execute`. - However, the :meth:`.ValuesBase.values` method can be used to "fix" a - particular set of parameters into the statement. - - Multiple calls to :meth:`.ValuesBase.values` will produce a new - construct, each one with the parameter list modified to include - the new parameters sent. In the typical case of a single - dictionary of parameters, the newly passed keys will replace - the same keys in the previous construct. In the case of a list-based - "multiple values" construct, each new list of values is extended - onto the existing list of values. - - :param \**kwargs: key value pairs representing the string key - of a :class:`_schema.Column` - mapped to the value to be rendered into the - VALUES or SET clause:: - - users.insert().values(name="some name") - - users.update().where(users.c.id==5).values(name="some name") - - :param \*args: As an alternative to passing key/value parameters, - a dictionary, tuple, or list of dictionaries or tuples can be passed - as a single positional argument in order to form the VALUES or - SET clause of the statement. The forms that are accepted vary - based on whether this is an :class:`_expression.Insert` or an - :class:`_expression.Update` construct. - - For either an :class:`_expression.Insert` or - :class:`_expression.Update` - construct, a single dictionary can be passed, which works the same as - that of the kwargs form:: - - users.insert().values({"name": "some name"}) - - users.update().values({"name": "some new name"}) - - Also for either form but more typically for the - :class:`_expression.Insert` construct, a tuple that contains an - entry for every column in the table is also accepted:: - - users.insert().values((5, "some name")) - - The :class:`_expression.Insert` construct also supports being - passed a list of dictionaries or full-table-tuples, which on the - server will render the less common SQL syntax of "multiple values" - - this syntax is supported on backends such as SQLite, PostgreSQL, - MySQL, but not necessarily others:: - - users.insert().values([ - {"name": "some name"}, - {"name": "some other name"}, - {"name": "yet another name"}, - ]) - - The above form would render a multiple VALUES statement similar to:: - - INSERT INTO users (name) VALUES - (:name_1), - (:name_2), - (:name_3) - - It is essential to note that **passing multiple values is - NOT the same as using traditional executemany() form**. The above - syntax is a **special** syntax not typically used. To emit an - INSERT statement against multiple rows, the normal method is - to pass a multiple values list to the - :meth:`_engine.Connection.execute` - method, which is supported by all database backends and is generally - more efficient for a very large number of parameters. - - .. seealso:: - - :ref:`tutorial_multiple_parameters` - an introduction to - the traditional Core method of multiple parameter set - invocation for INSERTs and other statements. - - The UPDATE construct also supports rendering the SET parameters - in a specific order. For this feature refer to the - :meth:`_expression.Update.ordered_values` method. - - .. seealso:: - - :meth:`_expression.Update.ordered_values` - - - """ - if args: - # positional case. this is currently expensive. we don't - # yet have positional-only args so we have to check the length. - # then we need to check multiparams vs. single dictionary. - # since the parameter format is needed in order to determine - # a cache key, we need to determine this up front. - arg = args[0] - - if kwargs: - raise exc.ArgumentError( - "Can't pass positional and kwargs to values() " - "simultaneously" - ) - elif len(args) > 1: - raise exc.ArgumentError( - "Only a single dictionary/tuple or list of " - "dictionaries/tuples is accepted positionally." - ) - - elif isinstance(arg, collections_abc.Sequence): - if arg and isinstance(arg[0], dict): - multi_kv_generator = DMLState.get_plugin_class( - self - )._get_multi_crud_kv_pairs - self._multi_values += (multi_kv_generator(self, arg),) - return self - - if arg and isinstance(arg[0], (list, tuple)): - self._multi_values += (arg,) - return self - - if TYPE_CHECKING: - # crud.py raises during compilation if this is not the - # case - assert isinstance(self, Insert) - - # tuple values - arg = {c.key: value for c, value in zip(self.table.c, arg)} - - else: - # kwarg path. this is the most common path for non-multi-params - # so this is fairly quick. - arg = cast("Dict[_DMLColumnArgument, Any]", kwargs) - if args: - raise exc.ArgumentError( - "Only a single dictionary/tuple or list of " - "dictionaries/tuples is accepted positionally." - ) - - # for top level values(), convert literals to anonymous bound - # parameters at statement construction time, so that these values can - # participate in the cache key process like any other ClauseElement. - # crud.py now intercepts bound parameters with unique=True from here - # and ensures they get the "crud"-style name when rendered. - - kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs - coerced_arg = dict(kv_generator(self, arg.items(), True)) - if self._values: - self._values = self._values.union(coerced_arg) - else: - self._values = util.immutabledict(coerced_arg) - return self - - -class Insert(ValuesBase): - """Represent an INSERT construct. - - The :class:`_expression.Insert` object is created using the - :func:`_expression.insert()` function. - - """ - - __visit_name__ = "insert" - - _supports_multi_parameters = True - - select = None - include_insert_from_select_defaults = False - - _sort_by_parameter_order: bool = False - - is_insert = True - - table: TableClause - - _traverse_internals = ( - [ - ("table", InternalTraversal.dp_clauseelement), - ("_inline", InternalTraversal.dp_boolean), - ("_select_names", InternalTraversal.dp_string_list), - ("_values", InternalTraversal.dp_dml_values), - ("_multi_values", InternalTraversal.dp_dml_multi_values), - ("select", InternalTraversal.dp_clauseelement), - ("_post_values_clause", InternalTraversal.dp_clauseelement), - ("_returning", InternalTraversal.dp_clauseelement_tuple), - ("_hints", InternalTraversal.dp_table_hint_list), - ("_return_defaults", InternalTraversal.dp_boolean), - ( - "_return_defaults_columns", - InternalTraversal.dp_clauseelement_tuple, - ), - ("_sort_by_parameter_order", InternalTraversal.dp_boolean), - ] - + HasPrefixes._has_prefixes_traverse_internals - + DialectKWArgs._dialect_kwargs_traverse_internals - + Executable._executable_traverse_internals - + HasCTE._has_ctes_traverse_internals - ) - - def __init__(self, table: _DMLTableArgument): - super().__init__(table) - - @_generative - def inline(self) -> Self: - """Make this :class:`_expression.Insert` construct "inline" . - - When set, 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. - - - .. versionchanged:: 1.4 the :paramref:`_expression.Insert.inline` - parameter - is now superseded by the :meth:`_expression.Insert.inline` method. - - """ - self._inline = True - return self - - @_generative - def from_select( - self, - names: Sequence[_DMLColumnArgument], - select: Selectable, - include_defaults: bool = True, - ) -> Self: - """Return a new :class:`_expression.Insert` construct which represents - an ``INSERT...FROM SELECT`` statement. - - e.g.:: - - sel = select(table1.c.a, table1.c.b).where(table1.c.c > 5) - ins = table2.insert().from_select(['a', 'b'], sel) - - :param names: a sequence of string column names or - :class:`_schema.Column` - objects representing the target columns. - :param select: a :func:`_expression.select` construct, - :class:`_expression.FromClause` - or other construct which resolves into a - :class:`_expression.FromClause`, - such as an ORM :class:`_query.Query` object, etc. The order of - columns returned from this FROM clause should correspond to the - order of columns sent as the ``names`` parameter; while this - is not checked before passing along to the database, the database - would normally raise an exception if these column lists don't - correspond. - :param include_defaults: if True, non-server default values and - SQL expressions as specified on :class:`_schema.Column` objects - (as documented in :ref:`metadata_defaults_toplevel`) not - otherwise specified in the list of names will be rendered - into the INSERT and SELECT statements, so that these values are also - included in the data to be inserted. - - .. note:: A Python-side default that uses a Python callable function - will only be invoked **once** for the whole statement, and **not - per row**. - - """ - - if self._values: - raise exc.InvalidRequestError( - "This construct already inserts value expressions" - ) - - self._select_names = [ - coercions.expect(roles.DMLColumnRole, name, as_key=True) - for name in names - ] - self._inline = True - self.include_insert_from_select_defaults = include_defaults - self.select = coercions.expect(roles.DMLSelectRole, select) - return self - - if TYPE_CHECKING: - # START OVERLOADED FUNCTIONS self.returning ReturningInsert 1-8 ", *, sort_by_parameter_order: bool = False" # noqa: E501 - - # code within this block is **programmatically, - # statically generated** by tools/generate_tuple_map_overloads.py - - @overload - def returning( - self, __ent0: _TCCA[_T0], *, sort_by_parameter_order: bool = False - ) -> ReturningInsert[Tuple[_T0]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1, _T2]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - __ent7: _TCCA[_T7], - *, - sort_by_parameter_order: bool = False, - ) -> ReturningInsert[ - Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7] - ]: ... - - # END OVERLOADED FUNCTIONS self.returning - - @overload - def returning( - self, - *cols: _ColumnsClauseArgument[Any], - sort_by_parameter_order: bool = False, - **__kw: Any, - ) -> ReturningInsert[Any]: ... - - def returning( - self, - *cols: _ColumnsClauseArgument[Any], - sort_by_parameter_order: bool = False, - **__kw: Any, - ) -> ReturningInsert[Any]: ... - - -class ReturningInsert(Insert, TypedReturnsRows[_TP]): - """Typing-only class that establishes a generic type form of - :class:`.Insert` which tracks returned column types. - - This datatype is delivered when calling the - :meth:`.Insert.returning` method. - - .. versionadded:: 2.0 - - """ - - -class DMLWhereBase: - table: _DMLTableElement - _where_criteria: Tuple[ColumnElement[Any], ...] = () - - @_generative - def where(self, *whereclause: _ColumnExpressionArgument[bool]) -> Self: - """Return a new construct with the given expression(s) added to - its WHERE clause, joined to the existing clause via AND, if any. - - Both :meth:`_dml.Update.where` and :meth:`_dml.Delete.where` - support multiple-table forms, including database-specific - ``UPDATE...FROM`` as well as ``DELETE..USING``. For backends that - don't have multiple-table support, a backend agnostic approach - to using multiple tables is to make use of correlated subqueries. - See the linked tutorial sections below for examples. - - .. seealso:: - - :ref:`tutorial_correlated_updates` - - :ref:`tutorial_update_from` - - :ref:`tutorial_multi_table_deletes` - - """ - - for criterion in whereclause: - where_criteria: ColumnElement[Any] = coercions.expect( - roles.WhereHavingRole, criterion, apply_propagate_attrs=self - ) - self._where_criteria += (where_criteria,) - return self - - def filter(self, *criteria: roles.ExpressionElementRole[Any]) -> Self: - """A synonym for the :meth:`_dml.DMLWhereBase.where` method. - - .. versionadded:: 1.4 - - """ - - return self.where(*criteria) - - def _filter_by_zero(self) -> _DMLTableElement: - return self.table - - def filter_by(self, **kwargs: Any) -> Self: - r"""apply the given filtering criterion as a WHERE clause - to this select. - - """ - from_entity = self._filter_by_zero() - - clauses = [ - _entity_namespace_key(from_entity, key) == value - for key, value in kwargs.items() - ] - return self.filter(*clauses) - - @property - def whereclause(self) -> Optional[ColumnElement[Any]]: - """Return the completed WHERE clause for this :class:`.DMLWhereBase` - statement. - - This assembles the current collection of WHERE criteria - into a single :class:`_expression.BooleanClauseList` construct. - - - .. versionadded:: 1.4 - - """ - - return BooleanClauseList._construct_for_whereclause( - self._where_criteria - ) - - -class Update(DMLWhereBase, ValuesBase): - """Represent an Update construct. - - The :class:`_expression.Update` object is created using the - :func:`_expression.update()` function. - - """ - - __visit_name__ = "update" - - is_update = True - - _traverse_internals = ( - [ - ("table", InternalTraversal.dp_clauseelement), - ("_where_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_inline", InternalTraversal.dp_boolean), - ("_ordered_values", InternalTraversal.dp_dml_ordered_values), - ("_values", InternalTraversal.dp_dml_values), - ("_returning", InternalTraversal.dp_clauseelement_tuple), - ("_hints", InternalTraversal.dp_table_hint_list), - ("_return_defaults", InternalTraversal.dp_boolean), - ( - "_return_defaults_columns", - InternalTraversal.dp_clauseelement_tuple, - ), - ] - + HasPrefixes._has_prefixes_traverse_internals - + DialectKWArgs._dialect_kwargs_traverse_internals - + Executable._executable_traverse_internals - + HasCTE._has_ctes_traverse_internals - ) - - def __init__(self, table: _DMLTableArgument): - super().__init__(table) - - @_generative - def ordered_values(self, *args: Tuple[_DMLColumnArgument, Any]) -> Self: - """Specify the VALUES clause of this UPDATE statement with an explicit - parameter ordering that will be maintained in the SET clause of the - resulting UPDATE statement. - - E.g.:: - - stmt = table.update().ordered_values( - ("name", "ed"), ("ident", "foo") - ) - - .. seealso:: - - :ref:`tutorial_parameter_ordered_updates` - full example of the - :meth:`_expression.Update.ordered_values` method. - - .. versionchanged:: 1.4 The :meth:`_expression.Update.ordered_values` - method - supersedes the - :paramref:`_expression.update.preserve_parameter_order` - parameter, which will be removed in SQLAlchemy 2.0. - - """ - if self._values: - raise exc.ArgumentError( - "This statement already has values present" - ) - elif self._ordered_values: - raise exc.ArgumentError( - "This statement already has ordered values present" - ) - - kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs - self._ordered_values = kv_generator(self, args, True) - return self - - @_generative - def inline(self) -> Self: - """Make this :class:`_expression.Update` construct "inline" . - - When set, SQL defaults present on :class:`_schema.Column` - objects via the - ``default`` keyword will be compiled 'inline' into the statement and - not pre-executed. This means that their values will not be available - in the dictionary returned from - :meth:`_engine.CursorResult.last_updated_params`. - - .. versionchanged:: 1.4 the :paramref:`_expression.update.inline` - parameter - is now superseded by the :meth:`_expression.Update.inline` method. - - """ - self._inline = True - return self - - if TYPE_CHECKING: - # START OVERLOADED FUNCTIONS self.returning ReturningUpdate 1-8 - - # code within this block is **programmatically, - # statically generated** by tools/generate_tuple_map_overloads.py - - @overload - def returning( - self, __ent0: _TCCA[_T0] - ) -> ReturningUpdate[Tuple[_T0]]: ... - - @overload - def returning( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1] - ) -> ReturningUpdate[Tuple[_T0, _T1]]: ... - - @overload - def returning( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2] - ) -> ReturningUpdate[Tuple[_T0, _T1, _T2]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - __ent7: _TCCA[_T7], - ) -> ReturningUpdate[ - Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7] - ]: ... - - # END OVERLOADED FUNCTIONS self.returning - - @overload - def returning( - self, *cols: _ColumnsClauseArgument[Any], **__kw: Any - ) -> ReturningUpdate[Any]: ... - - def returning( - self, *cols: _ColumnsClauseArgument[Any], **__kw: Any - ) -> ReturningUpdate[Any]: ... - - -class ReturningUpdate(Update, TypedReturnsRows[_TP]): - """Typing-only class that establishes a generic type form of - :class:`.Update` which tracks returned column types. - - This datatype is delivered when calling the - :meth:`.Update.returning` method. - - .. versionadded:: 2.0 - - """ - - -class Delete(DMLWhereBase, UpdateBase): - """Represent a DELETE construct. - - The :class:`_expression.Delete` object is created using the - :func:`_expression.delete()` function. - - """ - - __visit_name__ = "delete" - - is_delete = True - - _traverse_internals = ( - [ - ("table", InternalTraversal.dp_clauseelement), - ("_where_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_returning", InternalTraversal.dp_clauseelement_tuple), - ("_hints", InternalTraversal.dp_table_hint_list), - ] - + HasPrefixes._has_prefixes_traverse_internals - + DialectKWArgs._dialect_kwargs_traverse_internals - + Executable._executable_traverse_internals - + HasCTE._has_ctes_traverse_internals - ) - - def __init__(self, table: _DMLTableArgument): - self.table = coercions.expect( - roles.DMLTableRole, table, apply_propagate_attrs=self - ) - - if TYPE_CHECKING: - # START OVERLOADED FUNCTIONS self.returning ReturningDelete 1-8 - - # code within this block is **programmatically, - # statically generated** by tools/generate_tuple_map_overloads.py - - @overload - def returning( - self, __ent0: _TCCA[_T0] - ) -> ReturningDelete[Tuple[_T0]]: ... - - @overload - def returning( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1] - ) -> ReturningDelete[Tuple[_T0, _T1]]: ... - - @overload - def returning( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2] - ) -> ReturningDelete[Tuple[_T0, _T1, _T2]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ... - - @overload - def returning( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - __ent7: _TCCA[_T7], - ) -> ReturningDelete[ - Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7] - ]: ... - - # END OVERLOADED FUNCTIONS self.returning - - @overload - def returning( - self, *cols: _ColumnsClauseArgument[Any], **__kw: Any - ) -> ReturningDelete[Any]: ... - - def returning( - self, *cols: _ColumnsClauseArgument[Any], **__kw: Any - ) -> ReturningDelete[Any]: ... - - -class ReturningDelete(Update, TypedReturnsRows[_TP]): - """Typing-only class that establishes a generic type form of - :class:`.Delete` which tracks returned column types. - - This datatype is delivered when calling the - :meth:`.Delete.returning` method. - - .. versionadded:: 2.0 - - """ diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py deleted file mode 100644 index bafb5c7..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py +++ /dev/null @@ -1,5405 +0,0 @@ -# sql/elements.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""Core SQL expression elements, including :class:`_expression.ClauseElement`, -:class:`_expression.ColumnElement`, and derived classes. - -""" - -from __future__ import annotations - -from decimal import Decimal -from enum import IntEnum -import itertools -import operator -import re -import typing -from typing import AbstractSet -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import FrozenSet -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Set -from typing import Tuple as typing_Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import coercions -from . import operators -from . import roles -from . import traversals -from . import type_api -from ._typing import has_schema_attr -from ._typing import is_named_from_clause -from ._typing import is_quoted_name -from ._typing import is_tuple_type -from .annotation import Annotated -from .annotation import SupportsWrappingAnnotations -from .base import _clone -from .base import _expand_cloned -from .base import _generative -from .base import _NoArg -from .base import Executable -from .base import Generative -from .base import HasMemoized -from .base import Immutable -from .base import NO_ARG -from .base import SingletonConstant -from .cache_key import MemoizedHasCacheKey -from .cache_key import NO_CACHE -from .coercions import _document_text_coercion # noqa -from .operators import ColumnOperators -from .traversals import HasCopyInternals -from .visitors import cloned_traverse -from .visitors import ExternallyTraversible -from .visitors import InternalTraversal -from .visitors import traverse -from .visitors import Visitable -from .. import exc -from .. import inspection -from .. import util -from ..util import HasMemoized_ro_memoized_attribute -from ..util import TypingOnly -from ..util.typing import Literal -from ..util.typing import Self - -if typing.TYPE_CHECKING: - from ._typing import _ByArgument - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnExpressionOrStrLabelArgument - from ._typing import _HasDialect - from ._typing import _InfoType - from ._typing import _PropagateAttrsType - from ._typing import _TypeEngineArgument - from .cache_key import _CacheKeyTraversalType - from .cache_key import CacheKey - from .compiler import Compiled - from .compiler import SQLCompiler - from .functions import FunctionElement - from .operators import OperatorType - from .schema import Column - from .schema import DefaultGenerator - from .schema import FetchedValue - from .schema import ForeignKey - from .selectable import _SelectIterable - from .selectable import FromClause - from .selectable import NamedFromClause - from .selectable import TextualSelect - from .sqltypes import TupleType - from .type_api import TypeEngine - from .visitors import _CloneCallableType - from .visitors import _TraverseInternalsType - from .visitors import anon_map - from ..engine import Connection - from ..engine import Dialect - from ..engine.interfaces import _CoreMultiExecuteParams - from ..engine.interfaces import CacheStats - from ..engine.interfaces import CompiledCacheType - from ..engine.interfaces import CoreExecuteOptionsParameter - from ..engine.interfaces import SchemaTranslateMapType - from ..engine.result import Result - -_NUMERIC = Union[float, Decimal] -_NUMBER = Union[float, int, Decimal] - -_T = TypeVar("_T", bound="Any") -_T_co = TypeVar("_T_co", bound=Any, covariant=True) -_OPT = TypeVar("_OPT", bound="Any") -_NT = TypeVar("_NT", bound="_NUMERIC") - -_NMT = TypeVar("_NMT", bound="_NUMBER") - - -@overload -def literal( - value: Any, - type_: _TypeEngineArgument[_T], - literal_execute: bool = False, -) -> BindParameter[_T]: ... - - -@overload -def literal( - value: _T, - type_: None = None, - literal_execute: bool = False, -) -> BindParameter[_T]: ... - - -@overload -def literal( - value: Any, - type_: Optional[_TypeEngineArgument[Any]] = None, - literal_execute: bool = False, -) -> BindParameter[Any]: ... - - -def literal( - value: Any, - type_: Optional[_TypeEngineArgument[Any]] = None, - literal_execute: bool = False, -) -> BindParameter[Any]: - r"""Return a literal clause, bound to a bind parameter. - - Literal clauses are created automatically when non- - :class:`_expression.ClauseElement` objects (such as strings, ints, dates, - etc.) are - used in a comparison operation with a :class:`_expression.ColumnElement` - subclass, - such as a :class:`~sqlalchemy.schema.Column` object. Use this function - to force the generation of a literal clause, which will be created as a - :class:`BindParameter` with a bound value. - - :param value: the value to be bound. Can be any Python object supported by - the underlying DB-API, or is translatable via the given type argument. - - :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` which will - provide bind-parameter translation for this literal. - - :param literal_execute: optional bool, when True, the SQL engine will - attempt to render the bound value directly in the SQL statement at - execution time rather than providing as a parameter value. - - .. versionadded:: 2.0 - - """ - return coercions.expect( - roles.LiteralValueRole, - value, - type_=type_, - literal_execute=literal_execute, - ) - - -def literal_column( - text: str, type_: Optional[_TypeEngineArgument[_T]] = None -) -> ColumnClause[_T]: - r"""Produce a :class:`.ColumnClause` object that has the - :paramref:`_expression.column.is_literal` flag set to True. - - :func:`_expression.literal_column` is similar to - :func:`_expression.column`, except that - it is more often used as a "standalone" column expression that renders - exactly as stated; while :func:`_expression.column` - stores a string name that - will be assumed to be part of a table and may be quoted as such, - :func:`_expression.literal_column` can be that, - or any other arbitrary column-oriented - expression. - - :param text: the text of the expression; can be any SQL expression. - Quoting rules will not be applied. To specify a column-name expression - which should be subject to quoting rules, use the :func:`column` - function. - - :param type\_: an optional :class:`~sqlalchemy.types.TypeEngine` - object which will - provide result-set translation and additional expression semantics for - this column. If left as ``None`` the type will be :class:`.NullType`. - - .. seealso:: - - :func:`_expression.column` - - :func:`_expression.text` - - :ref:`tutorial_select_arbitrary_text` - - """ - return ColumnClause(text, type_=type_, is_literal=True) - - -class CompilerElement(Visitable): - """base class for SQL elements that can be compiled to produce a - SQL string. - - .. versionadded:: 2.0 - - """ - - __slots__ = () - __visit_name__ = "compiler_element" - - supports_execution = False - - stringify_dialect = "default" - - @util.preload_module("sqlalchemy.engine.default") - @util.preload_module("sqlalchemy.engine.url") - def compile( - self, - bind: Optional[_HasDialect] = None, - dialect: Optional[Dialect] = None, - **kw: Any, - ) -> Compiled: - """Compile this SQL expression. - - The return value is a :class:`~.Compiled` object. - Calling ``str()`` or ``unicode()`` on the returned value will yield a - string representation of the result. The - :class:`~.Compiled` object also can return a - dictionary of bind parameter names and values - using the ``params`` accessor. - - :param bind: An :class:`.Connection` or :class:`.Engine` which - can provide a :class:`.Dialect` in order to generate a - :class:`.Compiled` object. If the ``bind`` and - ``dialect`` parameters are both omitted, a default SQL compiler - is used. - - :param column_keys: Used for INSERT and UPDATE statements, a list of - column names which should be present in the VALUES clause of the - compiled statement. If ``None``, all columns from the target table - object are rendered. - - :param dialect: A :class:`.Dialect` instance which can generate - a :class:`.Compiled` object. This argument takes precedence over - the ``bind`` argument. - - :param compile_kwargs: optional dictionary of additional parameters - that will be passed through to the compiler within all "visit" - methods. This allows any custom flag to be passed through to - a custom compilation construct, for example. It is also used - for the case of passing the ``literal_binds`` flag through:: - - from sqlalchemy.sql import table, column, select - - t = table('t', column('x')) - - s = select(t).where(t.c.x == 5) - - print(s.compile(compile_kwargs={"literal_binds": True})) - - .. seealso:: - - :ref:`faq_sql_expression_string` - - """ - - if dialect is None: - if bind: - dialect = bind.dialect - elif self.stringify_dialect == "default": - default = util.preloaded.engine_default - dialect = default.StrCompileDialect() - else: - url = util.preloaded.engine_url - dialect = url.URL.create( - self.stringify_dialect - ).get_dialect()() - - return self._compiler(dialect, **kw) - - def _compiler(self, dialect: Dialect, **kw: Any) -> Compiled: - """Return a compiler appropriate for this ClauseElement, given a - Dialect.""" - - if TYPE_CHECKING: - assert isinstance(self, ClauseElement) - return dialect.statement_compiler(dialect, self, **kw) - - def __str__(self) -> str: - return str(self.compile()) - - -@inspection._self_inspects -class ClauseElement( - SupportsWrappingAnnotations, - MemoizedHasCacheKey, - HasCopyInternals, - ExternallyTraversible, - CompilerElement, -): - """Base class for elements of a programmatically constructed SQL - expression. - - """ - - __visit_name__ = "clause" - - if TYPE_CHECKING: - - @util.memoized_property - def _propagate_attrs(self) -> _PropagateAttrsType: - """like annotations, however these propagate outwards liberally - as SQL constructs are built, and are set up at construction time. - - """ - ... - - else: - _propagate_attrs = util.EMPTY_DICT - - @util.ro_memoized_property - def description(self) -> Optional[str]: - return None - - _is_clone_of: Optional[Self] = None - - is_clause_element = True - is_selectable = False - is_dml = False - _is_column_element = False - _is_keyed_column_element = False - _is_table = False - _gen_static_annotations_cache_key = False - _is_textual = False - _is_from_clause = False - _is_returns_rows = False - _is_text_clause = False - _is_from_container = False - _is_select_container = False - _is_select_base = False - _is_select_statement = False - _is_bind_parameter = False - _is_clause_list = False - _is_lambda_element = False - _is_singleton_constant = False - _is_immutable = False - _is_star = False - - @property - def _order_by_label_element(self) -> Optional[Label[Any]]: - return None - - _cache_key_traversal: _CacheKeyTraversalType = None - - negation_clause: ColumnElement[bool] - - if typing.TYPE_CHECKING: - - def get_children( - self, *, omit_attrs: typing_Tuple[str, ...] = ..., **kw: Any - ) -> Iterable[ClauseElement]: ... - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [] - - def _set_propagate_attrs(self, values: Mapping[str, Any]) -> Self: - # usually, self._propagate_attrs is empty here. one case where it's - # not is a subquery against ORM select, that is then pulled as a - # property of an aliased class. should all be good - - # assert not self._propagate_attrs - - self._propagate_attrs = util.immutabledict(values) - return self - - def _clone(self, **kw: Any) -> Self: - """Create a shallow copy of this ClauseElement. - - This method may be used by a generative API. Its also used as - part of the "deep" copy afforded by a traversal that combines - the _copy_internals() method. - - """ - - skip = self._memoized_keys - c = self.__class__.__new__(self.__class__) - - if skip: - # ensure this iteration remains atomic - c.__dict__ = { - k: v for k, v in self.__dict__.copy().items() if k not in skip - } - else: - c.__dict__ = self.__dict__.copy() - - # this is a marker that helps to "equate" clauses to each other - # when a Select returns its list of FROM clauses. the cloning - # process leaves around a lot of remnants of the previous clause - # typically in the form of column expressions still attached to the - # old table. - cc = self._is_clone_of - c._is_clone_of = cc if cc is not None else self - return c - - def _negate_in_binary(self, negated_op, original_op): - """a hook to allow the right side of a binary expression to respond - to a negation of the binary expression. - - Used for the special case of expanding bind parameter with IN. - - """ - return self - - def _with_binary_element_type(self, type_): - """in the context of binary expression, convert the type of this - object to the one given. - - applies only to :class:`_expression.ColumnElement` classes. - - """ - return self - - @property - def _constructor(self): - """return the 'constructor' for this ClauseElement. - - This is for the purposes for creating a new object of - this type. Usually, its just the element's __class__. - However, the "Annotated" version of the object overrides - to return the class of its proxied element. - - """ - return self.__class__ - - @HasMemoized.memoized_attribute - def _cloned_set(self): - """Return the set consisting all cloned ancestors of this - ClauseElement. - - Includes this ClauseElement. This accessor tends to be used for - FromClause objects to identify 'equivalent' FROM clauses, regardless - of transformative operations. - - """ - s = util.column_set() - f: Optional[ClauseElement] = self - - # note this creates a cycle, asserted in test_memusage. however, - # turning this into a plain @property adds tends of thousands of method - # calls to Core / ORM performance tests, so the small overhead - # introduced by the relatively small amount of short term cycles - # produced here is preferable - while f is not None: - s.add(f) - f = f._is_clone_of - return s - - def _de_clone(self): - while self._is_clone_of is not None: - self = self._is_clone_of - return self - - @property - def entity_namespace(self): - raise AttributeError( - "This SQL expression has no entity namespace " - "with which to filter from." - ) - - def __getstate__(self): - d = self.__dict__.copy() - d.pop("_is_clone_of", None) - d.pop("_generate_cache_key", None) - return d - - def _execute_on_connection( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> Result[Any]: - if self.supports_execution: - if TYPE_CHECKING: - assert isinstance(self, Executable) - return connection._execute_clauseelement( - self, distilled_params, execution_options - ) - else: - raise exc.ObjectNotExecutableError(self) - - def _execute_on_scalar( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> Any: - """an additional hook for subclasses to provide a different - implementation for connection.scalar() vs. connection.execute(). - - .. versionadded:: 2.0 - - """ - return self._execute_on_connection( - connection, distilled_params, execution_options - ).scalar() - - def _get_embedded_bindparams(self) -> Sequence[BindParameter[Any]]: - """Return the list of :class:`.BindParameter` objects embedded in the - object. - - This accomplishes the same purpose as ``visitors.traverse()`` or - similar would provide, however by making use of the cache key - it takes advantage of memoization of the key to result in fewer - net method calls, assuming the statement is also going to be - executed. - - """ - - key = self._generate_cache_key() - if key is None: - bindparams: List[BindParameter[Any]] = [] - - traverse(self, {}, {"bindparam": bindparams.append}) - return bindparams - - else: - return key.bindparams - - def unique_params( - self, - __optionaldict: Optional[Dict[str, Any]] = None, - **kwargs: Any, - ) -> Self: - """Return a copy with :func:`_expression.bindparam` elements - replaced. - - Same functionality as :meth:`_expression.ClauseElement.params`, - except adds `unique=True` - to affected bind parameters so that multiple statements can be - used. - - """ - return self._replace_params(True, __optionaldict, kwargs) - - def params( - self, - __optionaldict: Optional[Mapping[str, Any]] = None, - **kwargs: Any, - ) -> Self: - """Return a copy with :func:`_expression.bindparam` elements - replaced. - - Returns a copy of this ClauseElement with - :func:`_expression.bindparam` - elements replaced with values taken from the given dictionary:: - - >>> clause = column('x') + bindparam('foo') - >>> print(clause.compile().params) - {'foo':None} - >>> print(clause.params({'foo':7}).compile().params) - {'foo':7} - - """ - return self._replace_params(False, __optionaldict, kwargs) - - def _replace_params( - self, - unique: bool, - optionaldict: Optional[Mapping[str, Any]], - kwargs: Dict[str, Any], - ) -> Self: - if optionaldict: - kwargs.update(optionaldict) - - def visit_bindparam(bind: BindParameter[Any]) -> None: - if bind.key in kwargs: - bind.value = kwargs[bind.key] - bind.required = False - if unique: - bind._convert_to_unique() - - return cloned_traverse( - self, - {"maintain_key": True, "detect_subquery_cols": True}, - {"bindparam": visit_bindparam}, - ) - - def compare(self, other: ClauseElement, **kw: Any) -> bool: - r"""Compare this :class:`_expression.ClauseElement` to - the given :class:`_expression.ClauseElement`. - - Subclasses should override the default behavior, which is a - straight identity comparison. - - \**kw are arguments consumed by subclass ``compare()`` methods and - may be used to modify the criteria for comparison - (see :class:`_expression.ColumnElement`). - - """ - return traversals.compare(self, other, **kw) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> ClauseElement: - """Apply a 'grouping' to this :class:`_expression.ClauseElement`. - - This method is overridden by subclasses to return a "grouping" - construct, i.e. parenthesis. In particular it's used by "binary" - expressions to provide a grouping around themselves when placed into a - larger expression, as well as by :func:`_expression.select` - constructs when placed into the FROM clause of another - :func:`_expression.select`. (Note that subqueries should be - normally created using the :meth:`_expression.Select.alias` method, - as many - platforms require nested SELECT statements to be named). - - As expressions are composed together, the application of - :meth:`self_group` is automatic - end-user code should never - need to use this method directly. Note that SQLAlchemy's - clause constructs take operator precedence into account - - so parenthesis might not be needed, for example, in - an expression like ``x OR (y AND z)`` - AND takes precedence - over OR. - - The base :meth:`self_group` method of - :class:`_expression.ClauseElement` - just returns self. - """ - return self - - def _ungroup(self) -> ClauseElement: - """Return this :class:`_expression.ClauseElement` - without any groupings. - """ - - return self - - def _compile_w_cache( - self, - dialect: Dialect, - *, - compiled_cache: Optional[CompiledCacheType], - column_keys: List[str], - for_executemany: bool = False, - schema_translate_map: Optional[SchemaTranslateMapType] = None, - **kw: Any, - ) -> typing_Tuple[ - Compiled, Optional[Sequence[BindParameter[Any]]], CacheStats - ]: - elem_cache_key: Optional[CacheKey] - - if compiled_cache is not None and dialect._supports_statement_cache: - elem_cache_key = self._generate_cache_key() - else: - elem_cache_key = None - - if elem_cache_key is not None: - if TYPE_CHECKING: - assert compiled_cache is not None - - cache_key, extracted_params = elem_cache_key - key = ( - dialect, - cache_key, - tuple(column_keys), - bool(schema_translate_map), - for_executemany, - ) - compiled_sql = compiled_cache.get(key) - - if compiled_sql is None: - cache_hit = dialect.CACHE_MISS - compiled_sql = self._compiler( - dialect, - cache_key=elem_cache_key, - column_keys=column_keys, - for_executemany=for_executemany, - schema_translate_map=schema_translate_map, - **kw, - ) - compiled_cache[key] = compiled_sql - else: - cache_hit = dialect.CACHE_HIT - else: - extracted_params = None - compiled_sql = self._compiler( - dialect, - cache_key=elem_cache_key, - column_keys=column_keys, - for_executemany=for_executemany, - schema_translate_map=schema_translate_map, - **kw, - ) - - if not dialect._supports_statement_cache: - cache_hit = dialect.NO_DIALECT_SUPPORT - elif compiled_cache is None: - cache_hit = dialect.CACHING_DISABLED - else: - cache_hit = dialect.NO_CACHE_KEY - - return compiled_sql, extracted_params, cache_hit - - def __invert__(self): - # undocumented element currently used by the ORM for - # relationship.contains() - if hasattr(self, "negation_clause"): - return self.negation_clause - else: - return self._negate() - - def _negate(self) -> ClauseElement: - grouped = self.self_group(against=operators.inv) - assert isinstance(grouped, ColumnElement) - return UnaryExpression(grouped, operator=operators.inv) - - def __bool__(self): - raise TypeError("Boolean value of this clause is not defined") - - def __repr__(self): - friendly = self.description - if friendly is None: - return object.__repr__(self) - else: - return "<%s.%s at 0x%x; %s>" % ( - self.__module__, - self.__class__.__name__, - id(self), - friendly, - ) - - -class DQLDMLClauseElement(ClauseElement): - """represents a :class:`.ClauseElement` that compiles to a DQL or DML - expression, not DDL. - - .. versionadded:: 2.0 - - """ - - if typing.TYPE_CHECKING: - - def _compiler(self, dialect: Dialect, **kw: Any) -> SQLCompiler: - """Return a compiler appropriate for this ClauseElement, given a - Dialect.""" - ... - - def compile( # noqa: A001 - self, - bind: Optional[_HasDialect] = None, - dialect: Optional[Dialect] = None, - **kw: Any, - ) -> SQLCompiler: ... - - -class CompilerColumnElement( - roles.DMLColumnRole, - roles.DDLConstraintColumnRole, - roles.ColumnsClauseRole, - CompilerElement, -): - """A compiler-only column element used for ad-hoc string compilations. - - .. versionadded:: 2.0 - - """ - - __slots__ = () - - _propagate_attrs = util.EMPTY_DICT - _is_collection_aggregate = False - - -# SQLCoreOperations should be suiting the ExpressionElementRole -# and ColumnsClauseRole. however the MRO issues become too elaborate -# at the moment. -class SQLCoreOperations(Generic[_T_co], ColumnOperators, TypingOnly): - __slots__ = () - - # annotations for comparison methods - # these are from operators->Operators / ColumnOperators, - # redefined with the specific types returned by ColumnElement hierarchies - if typing.TYPE_CHECKING: - - @util.non_memoized_property - def _propagate_attrs(self) -> _PropagateAttrsType: ... - - def operate( - self, op: OperatorType, *other: Any, **kwargs: Any - ) -> ColumnElement[Any]: ... - - def reverse_operate( - self, op: OperatorType, other: Any, **kwargs: Any - ) -> ColumnElement[Any]: ... - - @overload - def op( - self, - opstring: str, - precedence: int = ..., - is_comparison: bool = ..., - *, - return_type: _TypeEngineArgument[_OPT], - python_impl: Optional[Callable[..., Any]] = None, - ) -> Callable[[Any], BinaryExpression[_OPT]]: ... - - @overload - def op( - self, - opstring: str, - precedence: int = ..., - is_comparison: bool = ..., - return_type: Optional[_TypeEngineArgument[Any]] = ..., - python_impl: Optional[Callable[..., Any]] = ..., - ) -> Callable[[Any], BinaryExpression[Any]]: ... - - def op( - self, - opstring: str, - precedence: int = 0, - is_comparison: bool = False, - return_type: Optional[_TypeEngineArgument[Any]] = None, - python_impl: Optional[Callable[..., Any]] = None, - ) -> Callable[[Any], BinaryExpression[Any]]: ... - - def bool_op( - self, - opstring: str, - precedence: int = 0, - python_impl: Optional[Callable[..., Any]] = None, - ) -> Callable[[Any], BinaryExpression[bool]]: ... - - def __and__(self, other: Any) -> BooleanClauseList: ... - - def __or__(self, other: Any) -> BooleanClauseList: ... - - def __invert__(self) -> ColumnElement[_T_co]: ... - - def __lt__(self, other: Any) -> ColumnElement[bool]: ... - - def __le__(self, other: Any) -> ColumnElement[bool]: ... - - # declare also that this class has an hash method otherwise - # it may be assumed to be None by type checkers since the - # object defines __eq__ and python sets it to None in that case: - # https://docs.python.org/3/reference/datamodel.html#object.__hash__ - def __hash__(self) -> int: ... - - def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501 - ... - - def __ne__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501 - ... - - def is_distinct_from(self, other: Any) -> ColumnElement[bool]: ... - - def is_not_distinct_from(self, other: Any) -> ColumnElement[bool]: ... - - def __gt__(self, other: Any) -> ColumnElement[bool]: ... - - def __ge__(self, other: Any) -> ColumnElement[bool]: ... - - def __neg__(self) -> UnaryExpression[_T_co]: ... - - def __contains__(self, other: Any) -> ColumnElement[bool]: ... - - def __getitem__(self, index: Any) -> ColumnElement[Any]: ... - - @overload - def __lshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ... - - @overload - def __lshift__(self, other: Any) -> ColumnElement[Any]: ... - - def __lshift__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rshift__(self: _SQO[int], other: Any) -> ColumnElement[int]: ... - - @overload - def __rshift__(self, other: Any) -> ColumnElement[Any]: ... - - def __rshift__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def concat(self: _SQO[str], other: Any) -> ColumnElement[str]: ... - - @overload - def concat(self, other: Any) -> ColumnElement[Any]: ... - - def concat(self, other: Any) -> ColumnElement[Any]: ... - - def like( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def ilike( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def bitwise_xor(self, other: Any) -> BinaryExpression[Any]: ... - - def bitwise_or(self, other: Any) -> BinaryExpression[Any]: ... - - def bitwise_and(self, other: Any) -> BinaryExpression[Any]: ... - - def bitwise_not(self) -> UnaryExpression[_T_co]: ... - - def bitwise_lshift(self, other: Any) -> BinaryExpression[Any]: ... - - def bitwise_rshift(self, other: Any) -> BinaryExpression[Any]: ... - - def in_( - self, - other: Union[ - Iterable[Any], BindParameter[Any], roles.InElementRole - ], - ) -> BinaryExpression[bool]: ... - - def not_in( - self, - other: Union[ - Iterable[Any], BindParameter[Any], roles.InElementRole - ], - ) -> BinaryExpression[bool]: ... - - def notin_( - self, - other: Union[ - Iterable[Any], BindParameter[Any], roles.InElementRole - ], - ) -> BinaryExpression[bool]: ... - - def not_like( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def notlike( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def not_ilike( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def notilike( - self, other: Any, escape: Optional[str] = None - ) -> BinaryExpression[bool]: ... - - def is_(self, other: Any) -> BinaryExpression[bool]: ... - - def is_not(self, other: Any) -> BinaryExpression[bool]: ... - - def isnot(self, other: Any) -> BinaryExpression[bool]: ... - - def startswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnElement[bool]: ... - - def istartswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnElement[bool]: ... - - def endswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnElement[bool]: ... - - def iendswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnElement[bool]: ... - - def contains(self, other: Any, **kw: Any) -> ColumnElement[bool]: ... - - def icontains(self, other: Any, **kw: Any) -> ColumnElement[bool]: ... - - def match(self, other: Any, **kwargs: Any) -> ColumnElement[bool]: ... - - def regexp_match( - self, pattern: Any, flags: Optional[str] = None - ) -> ColumnElement[bool]: ... - - def regexp_replace( - self, pattern: Any, replacement: Any, flags: Optional[str] = None - ) -> ColumnElement[str]: ... - - def desc(self) -> UnaryExpression[_T_co]: ... - - def asc(self) -> UnaryExpression[_T_co]: ... - - def nulls_first(self) -> UnaryExpression[_T_co]: ... - - def nullsfirst(self) -> UnaryExpression[_T_co]: ... - - def nulls_last(self) -> UnaryExpression[_T_co]: ... - - def nullslast(self) -> UnaryExpression[_T_co]: ... - - def collate(self, collation: str) -> CollationClause: ... - - def between( - self, cleft: Any, cright: Any, symmetric: bool = False - ) -> BinaryExpression[bool]: ... - - def distinct(self: _SQO[_T_co]) -> UnaryExpression[_T_co]: ... - - def any_(self) -> CollectionAggregate[Any]: ... - - def all_(self) -> CollectionAggregate[Any]: ... - - # numeric overloads. These need more tweaking - # in particular they all need to have a variant for Optiona[_T] - # because Optional only applies to the data side, not the expression - # side - - @overload - def __add__( - self: _SQO[_NMT], - other: Any, - ) -> ColumnElement[_NMT]: ... - - @overload - def __add__( - self: _SQO[str], - other: Any, - ) -> ColumnElement[str]: ... - - def __add__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __radd__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ... - - @overload - def __radd__(self: _SQO[str], other: Any) -> ColumnElement[str]: ... - - def __radd__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __sub__( - self: _SQO[_NMT], - other: Any, - ) -> ColumnElement[_NMT]: ... - - @overload - def __sub__(self, other: Any) -> ColumnElement[Any]: ... - - def __sub__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rsub__( - self: _SQO[_NMT], - other: Any, - ) -> ColumnElement[_NMT]: ... - - @overload - def __rsub__(self, other: Any) -> ColumnElement[Any]: ... - - def __rsub__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __mul__( - self: _SQO[_NMT], - other: Any, - ) -> ColumnElement[_NMT]: ... - - @overload - def __mul__(self, other: Any) -> ColumnElement[Any]: ... - - def __mul__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rmul__( - self: _SQO[_NMT], - other: Any, - ) -> ColumnElement[_NMT]: ... - - @overload - def __rmul__(self, other: Any) -> ColumnElement[Any]: ... - - def __rmul__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __mod__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ... - - @overload - def __mod__(self, other: Any) -> ColumnElement[Any]: ... - - def __mod__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rmod__(self: _SQO[_NMT], other: Any) -> ColumnElement[_NMT]: ... - - @overload - def __rmod__(self, other: Any) -> ColumnElement[Any]: ... - - def __rmod__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __truediv__( - self: _SQO[int], other: Any - ) -> ColumnElement[_NUMERIC]: ... - - @overload - def __truediv__(self: _SQO[_NT], other: Any) -> ColumnElement[_NT]: ... - - @overload - def __truediv__(self, other: Any) -> ColumnElement[Any]: ... - - def __truediv__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rtruediv__( - self: _SQO[_NMT], other: Any - ) -> ColumnElement[_NUMERIC]: ... - - @overload - def __rtruediv__(self, other: Any) -> ColumnElement[Any]: ... - - def __rtruediv__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __floordiv__( - self: _SQO[_NMT], other: Any - ) -> ColumnElement[_NMT]: ... - - @overload - def __floordiv__(self, other: Any) -> ColumnElement[Any]: ... - - def __floordiv__(self, other: Any) -> ColumnElement[Any]: ... - - @overload - def __rfloordiv__( - self: _SQO[_NMT], other: Any - ) -> ColumnElement[_NMT]: ... - - @overload - def __rfloordiv__(self, other: Any) -> ColumnElement[Any]: ... - - def __rfloordiv__(self, other: Any) -> ColumnElement[Any]: ... - - -class SQLColumnExpression( - SQLCoreOperations[_T_co], roles.ExpressionElementRole[_T_co], TypingOnly -): - """A type that may be used to indicate any SQL column element or object - that acts in place of one. - - :class:`.SQLColumnExpression` is a base of - :class:`.ColumnElement`, as well as within the bases of ORM elements - such as :class:`.InstrumentedAttribute`, and may be used in :pep:`484` - typing to indicate arguments or return values that should behave - as column expressions. - - .. versionadded:: 2.0.0b4 - - - """ - - __slots__ = () - - -_SQO = SQLCoreOperations - - -class ColumnElement( - roles.ColumnArgumentOrKeyRole, - roles.StatementOptionRole, - roles.WhereHavingRole, - roles.BinaryElementRole[_T], - roles.OrderByRole, - roles.ColumnsClauseRole, - roles.LimitOffsetRole, - roles.DMLColumnRole, - roles.DDLConstraintColumnRole, - roles.DDLExpressionRole, - SQLColumnExpression[_T], - DQLDMLClauseElement, -): - """Represent a column-oriented SQL expression suitable for usage in the - "columns" clause, WHERE clause etc. of a statement. - - While the most familiar kind of :class:`_expression.ColumnElement` is the - :class:`_schema.Column` object, :class:`_expression.ColumnElement` - serves as the basis - for any unit that may be present in a SQL expression, including - the expressions themselves, SQL functions, bound parameters, - literal expressions, keywords such as ``NULL``, etc. - :class:`_expression.ColumnElement` - is the ultimate base class for all such elements. - - A wide variety of SQLAlchemy Core functions work at the SQL expression - level, and are intended to accept instances of - :class:`_expression.ColumnElement` as - arguments. These functions will typically document that they accept a - "SQL expression" as an argument. What this means in terms of SQLAlchemy - usually refers to an input which is either already in the form of a - :class:`_expression.ColumnElement` object, - or a value which can be **coerced** into - one. The coercion rules followed by most, but not all, SQLAlchemy Core - functions with regards to SQL expressions are as follows: - - * a literal Python value, such as a string, integer or floating - point value, boolean, datetime, ``Decimal`` object, or virtually - any other Python object, will be coerced into a "literal bound - value". This generally means that a :func:`.bindparam` will be - produced featuring the given value embedded into the construct; the - resulting :class:`.BindParameter` object is an instance of - :class:`_expression.ColumnElement`. - The Python value will ultimately be sent - to the DBAPI at execution time as a parameterized argument to the - ``execute()`` or ``executemany()`` methods, after SQLAlchemy - type-specific converters (e.g. those provided by any associated - :class:`.TypeEngine` objects) are applied to the value. - - * any special object value, typically ORM-level constructs, which - feature an accessor called ``__clause_element__()``. The Core - expression system looks for this method when an object of otherwise - unknown type is passed to a function that is looking to coerce the - argument into a :class:`_expression.ColumnElement` and sometimes a - :class:`_expression.SelectBase` expression. - It is used within the ORM to - convert from ORM-specific objects like mapped classes and - mapped attributes into Core expression objects. - - * The Python ``None`` value is typically interpreted as ``NULL``, - which in SQLAlchemy Core produces an instance of :func:`.null`. - - A :class:`_expression.ColumnElement` provides the ability to generate new - :class:`_expression.ColumnElement` - objects using Python expressions. This means that Python operators - such as ``==``, ``!=`` and ``<`` are overloaded to mimic SQL operations, - and allow the instantiation of further :class:`_expression.ColumnElement` - instances - which are composed from other, more fundamental - :class:`_expression.ColumnElement` - objects. For example, two :class:`.ColumnClause` objects can be added - together with the addition operator ``+`` to produce - a :class:`.BinaryExpression`. - Both :class:`.ColumnClause` and :class:`.BinaryExpression` are subclasses - of :class:`_expression.ColumnElement`: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy.sql import column - >>> column('a') + column('b') - <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> - >>> print(column('a') + column('b')) - {printsql}a + b - - .. seealso:: - - :class:`_schema.Column` - - :func:`_expression.column` - - """ - - __visit_name__ = "column_element" - - primary_key: bool = False - _is_clone_of: Optional[ColumnElement[_T]] - _is_column_element = True - _insert_sentinel: bool = False - _omit_from_statements = False - _is_collection_aggregate = False - - foreign_keys: AbstractSet[ForeignKey] = frozenset() - - @util.memoized_property - def _proxies(self) -> List[ColumnElement[Any]]: - return [] - - @util.non_memoized_property - def _tq_label(self) -> Optional[str]: - """The named label that can be used to target - this column in a result set in a "table qualified" context. - - This label is almost always the label used when - rendering <expr> AS <label> in a SELECT statement when using - the LABEL_STYLE_TABLENAME_PLUS_COL label style, which is what the - legacy ORM ``Query`` object uses as well. - - For a regular Column bound to a Table, this is typically the label - <tablename>_<columnname>. For other constructs, different rules - may apply, such as anonymized labels and others. - - .. versionchanged:: 1.4.21 renamed from ``._label`` - - """ - return None - - key: Optional[str] = None - """The 'key' that in some circumstances refers to this object in a - Python namespace. - - This typically refers to the "key" of the column as present in the - ``.c`` collection of a selectable, e.g. ``sometable.c["somekey"]`` would - return a :class:`_schema.Column` with a ``.key`` of "somekey". - - """ - - @HasMemoized.memoized_attribute - def _tq_key_label(self) -> Optional[str]: - """A label-based version of 'key' that in some circumstances refers - to this object in a Python namespace. - - - _tq_key_label comes into play when a select() statement is constructed - with apply_labels(); in this case, all Column objects in the ``.c`` - collection are rendered as <tablename>_<columnname> in SQL; this is - essentially the value of ._label. But to locate those columns in the - ``.c`` collection, the name is along the lines of <tablename>_<key>; - that's the typical value of .key_label. - - .. versionchanged:: 1.4.21 renamed from ``._key_label`` - - """ - return self._proxy_key - - @property - def _key_label(self) -> Optional[str]: - """legacy; renamed to _tq_key_label""" - return self._tq_key_label - - @property - def _label(self) -> Optional[str]: - """legacy; renamed to _tq_label""" - return self._tq_label - - @property - def _non_anon_label(self) -> Optional[str]: - """the 'name' that naturally applies this element when rendered in - SQL. - - Concretely, this is the "name" of a column or a label in a - SELECT statement; ``<columnname>`` and ``<labelname>`` below:: - - SELECT <columnmame> FROM table - - SELECT column AS <labelname> FROM table - - Above, the two names noted will be what's present in the DBAPI - ``cursor.description`` as the names. - - If this attribute returns ``None``, it means that the SQL element as - written does not have a 100% fully predictable "name" that would appear - in the ``cursor.description``. Examples include SQL functions, CAST - functions, etc. While such things do return names in - ``cursor.description``, they are only predictable on a - database-specific basis; e.g. an expression like ``MAX(table.col)`` may - appear as the string ``max`` on one database (like PostgreSQL) or may - appear as the whole expression ``max(table.col)`` on SQLite. - - The default implementation looks for a ``.name`` attribute on the - object, as has been the precedent established in SQLAlchemy for many - years. An exception is made on the ``FunctionElement`` subclass - so that the return value is always ``None``. - - .. versionadded:: 1.4.21 - - - - """ - return getattr(self, "name", None) - - _render_label_in_columns_clause = True - """A flag used by select._columns_plus_names that helps to determine - we are actually going to render in terms of "SELECT <col> AS <label>". - This flag can be returned as False for some Column objects that want - to be rendered as simple "SELECT <col>"; typically columns that don't have - any parent table and are named the same as what the label would be - in any case. - - """ - - _allow_label_resolve = True - """A flag that can be flipped to prevent a column from being resolvable - by string label name. - - The joined eager loader strategy in the ORM uses this, for example. - - """ - - _is_implicitly_boolean = False - - _alt_names: Sequence[str] = () - - @overload - def self_group( - self: ColumnElement[_T], against: Optional[OperatorType] = None - ) -> ColumnElement[_T]: ... - - @overload - def self_group( - self: ColumnElement[Any], against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: ... - - def self_group( - self, against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: - if ( - against in (operators.and_, operators.or_, operators._asbool) - and self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity - ): - return AsBoolean(self, operators.is_true, operators.is_false) - elif against in (operators.any_op, operators.all_op): - return Grouping(self) - else: - return self - - @overload - def _negate(self: ColumnElement[bool]) -> ColumnElement[bool]: ... - - @overload - def _negate(self: ColumnElement[_T]) -> ColumnElement[_T]: ... - - def _negate(self) -> ColumnElement[Any]: - if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity: - return AsBoolean(self, operators.is_false, operators.is_true) - else: - grouped = self.self_group(against=operators.inv) - assert isinstance(grouped, ColumnElement) - return UnaryExpression( - grouped, operator=operators.inv, wraps_column_expression=True - ) - - type: TypeEngine[_T] - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - # used for delayed setup of - # type_api - return type_api.NULLTYPE - - @HasMemoized.memoized_attribute - def comparator(self) -> TypeEngine.Comparator[_T]: - try: - comparator_factory = self.type.comparator_factory - except AttributeError as err: - raise TypeError( - "Object %r associated with '.type' attribute " - "is not a TypeEngine class or object" % self.type - ) from err - else: - return comparator_factory(self) - - def __setstate__(self, state): - self.__dict__.update(state) - - def __getattr__(self, key: str) -> Any: - try: - return getattr(self.comparator, key) - except AttributeError as err: - raise AttributeError( - "Neither %r object nor %r object has an attribute %r" - % ( - type(self).__name__, - type(self.comparator).__name__, - key, - ) - ) from err - - def operate( - self, - op: operators.OperatorType, - *other: Any, - **kwargs: Any, - ) -> ColumnElement[Any]: - return op(self.comparator, *other, **kwargs) # type: ignore[no-any-return] # noqa: E501 - - def reverse_operate( - self, op: operators.OperatorType, other: Any, **kwargs: Any - ) -> ColumnElement[Any]: - return op(other, self.comparator, **kwargs) # type: ignore[no-any-return] # noqa: E501 - - def _bind_param( - self, - operator: operators.OperatorType, - obj: Any, - type_: Optional[TypeEngine[_T]] = None, - expanding: bool = False, - ) -> BindParameter[_T]: - return BindParameter( - None, - obj, - _compared_to_operator=operator, - type_=type_, - _compared_to_type=self.type, - unique=True, - expanding=expanding, - ) - - @property - def expression(self) -> ColumnElement[Any]: - """Return a column expression. - - Part of the inspection interface; returns self. - - """ - return self - - @property - def _select_iterable(self) -> _SelectIterable: - return (self,) - - @util.memoized_property - def base_columns(self) -> FrozenSet[ColumnElement[Any]]: - return frozenset(c for c in self.proxy_set if not c._proxies) - - @util.memoized_property - def proxy_set(self) -> FrozenSet[ColumnElement[Any]]: - """set of all columns we are proxying - - as of 2.0 this is explicitly deannotated columns. previously it was - effectively deannotated columns but wasn't enforced. annotated - columns should basically not go into sets if at all possible because - their hashing behavior is very non-performant. - - """ - return frozenset([self._deannotate()]).union( - itertools.chain(*[c.proxy_set for c in self._proxies]) - ) - - @util.memoized_property - def _expanded_proxy_set(self) -> FrozenSet[ColumnElement[Any]]: - return frozenset(_expand_cloned(self.proxy_set)) - - def _uncached_proxy_list(self) -> List[ColumnElement[Any]]: - """An 'uncached' version of proxy set. - - This list includes annotated columns which perform very poorly in - set operations. - - """ - - return [self] + list( - itertools.chain(*[c._uncached_proxy_list() for c in self._proxies]) - ) - - def shares_lineage(self, othercolumn: ColumnElement[Any]) -> bool: - """Return True if the given :class:`_expression.ColumnElement` - has a common ancestor to this :class:`_expression.ColumnElement`.""" - - return bool(self.proxy_set.intersection(othercolumn.proxy_set)) - - def _compare_name_for_result(self, other: ColumnElement[Any]) -> bool: - """Return True if the given column element compares to this one - when targeting within a result row.""" - - return ( - hasattr(other, "name") - and hasattr(self, "name") - and other.name == self.name - ) - - @HasMemoized.memoized_attribute - def _proxy_key(self) -> Optional[str]: - if self._annotations and "proxy_key" in self._annotations: - return cast(str, self._annotations["proxy_key"]) - - name = self.key - if not name: - # there's a bit of a seeming contradiction which is that the - # "_non_anon_label" of a column can in fact be an - # "_anonymous_label"; this is when it's on a column that is - # proxying for an anonymous expression in a subquery. - name = self._non_anon_label - - if isinstance(name, _anonymous_label): - return None - else: - return name - - @HasMemoized.memoized_attribute - def _expression_label(self) -> Optional[str]: - """a suggested label to use in the case that the column has no name, - which should be used if possible as the explicit 'AS <label>' - where this expression would normally have an anon label. - - this is essentially mostly what _proxy_key does except it returns - None if the column has a normal name that can be used. - - """ - - if getattr(self, "name", None) is not None: - return None - elif self._annotations and "proxy_key" in self._annotations: - return cast(str, self._annotations["proxy_key"]) - else: - return None - - def _make_proxy( - self, - selectable: FromClause, - *, - name: Optional[str] = None, - key: Optional[str] = None, - name_is_truncatable: bool = False, - compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None, - **kw: Any, - ) -> typing_Tuple[str, ColumnClause[_T]]: - """Create a new :class:`_expression.ColumnElement` representing this - :class:`_expression.ColumnElement` as it appears in the select list of - a descending selectable. - - """ - if name is None: - name = self._anon_name_label - if key is None: - key = self._proxy_key - else: - key = name - - assert key is not None - - co: ColumnClause[_T] = ColumnClause( - ( - coercions.expect(roles.TruncatedLabelRole, name) - if name_is_truncatable - else name - ), - type_=getattr(self, "type", None), - _selectable=selectable, - ) - - co._propagate_attrs = selectable._propagate_attrs - if compound_select_cols: - co._proxies = list(compound_select_cols) - else: - co._proxies = [self] - if selectable._is_clone_of is not None: - co._is_clone_of = selectable._is_clone_of.columns.get(key) - return key, co - - def cast(self, type_: _TypeEngineArgument[_OPT]) -> Cast[_OPT]: - """Produce a type cast, i.e. ``CAST(<expression> AS <type>)``. - - This is a shortcut to the :func:`_expression.cast` function. - - .. seealso:: - - :ref:`tutorial_casts` - - :func:`_expression.cast` - - :func:`_expression.type_coerce` - - """ - return Cast(self, type_) - - def label(self, name: Optional[str]) -> Label[_T]: - """Produce a column label, i.e. ``<columnname> AS <name>``. - - This is a shortcut to the :func:`_expression.label` function. - - If 'name' is ``None``, an anonymous label name will be generated. - - """ - return Label(name, self, self.type) - - def _anon_label( - self, seed: Optional[str], add_hash: Optional[int] = None - ) -> _anonymous_label: - while self._is_clone_of is not None: - self = self._is_clone_of - - # as of 1.4 anonymous label for ColumnElement uses hash(), not id(), - # as the identifier, because a column and its annotated version are - # the same thing in a SQL statement - hash_value = hash(self) - - if add_hash: - # this path is used for disambiguating anon labels that would - # otherwise be the same name for the same element repeated. - # an additional numeric value is factored in for each label. - - # shift hash(self) (which is id(self), typically 8 byte integer) - # 16 bits leftward. fill extra add_hash on right - assert add_hash < (2 << 15) - assert seed - hash_value = (hash_value << 16) | add_hash - - # extra underscore is added for labels with extra hash - # values, to isolate the "deduped anon" namespace from the - # regular namespace. eliminates chance of these - # manufactured hash values overlapping with regular ones for some - # undefined python interpreter - seed = seed + "_" - - if isinstance(seed, _anonymous_label): - return _anonymous_label.safe_construct( - hash_value, "", enclosing_label=seed - ) - - return _anonymous_label.safe_construct(hash_value, seed or "anon") - - @util.memoized_property - def _anon_name_label(self) -> str: - """Provides a constant 'anonymous label' for this ColumnElement. - - This is a label() expression which will be named at compile time. - The same label() is returned each time ``anon_label`` is called so - that expressions can reference ``anon_label`` multiple times, - producing the same label name at compile time. - - The compiler uses this function automatically at compile time - for expressions that are known to be 'unnamed' like binary - expressions and function calls. - - .. versionchanged:: 1.4.9 - this attribute was not intended to be - public and is renamed to _anon_name_label. anon_name exists - for backwards compat - - """ - name = getattr(self, "name", None) - return self._anon_label(name) - - @util.memoized_property - def _anon_key_label(self) -> _anonymous_label: - """Provides a constant 'anonymous key label' for this ColumnElement. - - Compare to ``anon_label``, except that the "key" of the column, - if available, is used to generate the label. - - This is used when a deduplicating key is placed into the columns - collection of a selectable. - - .. versionchanged:: 1.4.9 - this attribute was not intended to be - public and is renamed to _anon_key_label. anon_key_label exists - for backwards compat - - """ - return self._anon_label(self._proxy_key) - - @property - @util.deprecated( - "1.4", - "The :attr:`_expression.ColumnElement.anon_label` attribute is now " - "private, and the public accessor is deprecated.", - ) - def anon_label(self) -> str: - return self._anon_name_label - - @property - @util.deprecated( - "1.4", - "The :attr:`_expression.ColumnElement.anon_key_label` attribute is " - "now private, and the public accessor is deprecated.", - ) - def anon_key_label(self) -> str: - return self._anon_key_label - - def _dedupe_anon_label_idx(self, idx: int) -> str: - """label to apply to a column that is anon labeled, but repeated - in the SELECT, so that we have to make an "extra anon" label that - disambiguates it from the previous appearance. - - these labels come out like "foo_bar_id__1" and have double underscores - in them. - - """ - label = getattr(self, "name", None) - - # current convention is that if the element doesn't have a - # ".name" (usually because it is not NamedColumn), we try to - # use a "table qualified" form for the "dedupe anon" label, - # based on the notion that a label like - # "CAST(casttest.v1 AS DECIMAL) AS casttest_v1__1" looks better than - # "CAST(casttest.v1 AS DECIMAL) AS anon__1" - - if label is None: - return self._dedupe_anon_tq_label_idx(idx) - else: - return self._anon_label(label, add_hash=idx) - - @util.memoized_property - def _anon_tq_label(self) -> _anonymous_label: - return self._anon_label(getattr(self, "_tq_label", None)) - - @util.memoized_property - def _anon_tq_key_label(self) -> _anonymous_label: - return self._anon_label(getattr(self, "_tq_key_label", None)) - - def _dedupe_anon_tq_label_idx(self, idx: int) -> _anonymous_label: - label = getattr(self, "_tq_label", None) or "anon" - - return self._anon_label(label, add_hash=idx) - - -class KeyedColumnElement(ColumnElement[_T]): - """ColumnElement where ``.key`` is non-None.""" - - _is_keyed_column_element = True - - key: str - - -class WrapsColumnExpression(ColumnElement[_T]): - """Mixin that defines a :class:`_expression.ColumnElement` - as a wrapper with special - labeling behavior for an expression that already has a name. - - .. versionadded:: 1.4 - - .. seealso:: - - :ref:`change_4449` - - - """ - - @property - def wrapped_column_expression(self) -> ColumnElement[_T]: - raise NotImplementedError() - - @util.non_memoized_property - def _tq_label(self) -> Optional[str]: - wce = self.wrapped_column_expression - if hasattr(wce, "_tq_label"): - return wce._tq_label - else: - return None - - @property - def _label(self) -> Optional[str]: - return self._tq_label - - @property - def _non_anon_label(self) -> Optional[str]: - return None - - @util.non_memoized_property - def _anon_name_label(self) -> str: - wce = self.wrapped_column_expression - - # this logic tries to get the WrappedColumnExpression to render - # with "<expr> AS <name>", where "<name>" is the natural name - # within the expression itself. e.g. "CAST(table.foo) AS foo". - if not wce._is_text_clause: - nal = wce._non_anon_label - if nal: - return nal - elif hasattr(wce, "_anon_name_label"): - return wce._anon_name_label - return super()._anon_name_label - - def _dedupe_anon_label_idx(self, idx: int) -> str: - wce = self.wrapped_column_expression - nal = wce._non_anon_label - if nal: - return self._anon_label(nal + "_") - else: - return self._dedupe_anon_tq_label_idx(idx) - - @property - def _proxy_key(self): - wce = self.wrapped_column_expression - - if not wce._is_text_clause: - return wce._proxy_key - return super()._proxy_key - - -class BindParameter(roles.InElementRole, KeyedColumnElement[_T]): - r"""Represent a "bound expression". - - :class:`.BindParameter` is invoked explicitly using the - :func:`.bindparam` function, as in:: - - from sqlalchemy import bindparam - - stmt = select(users_table).where( - users_table.c.name == bindparam("username") - ) - - Detailed discussion of how :class:`.BindParameter` is used is - at :func:`.bindparam`. - - .. seealso:: - - :func:`.bindparam` - - """ - - __visit_name__ = "bindparam" - - _traverse_internals: _TraverseInternalsType = [ - ("key", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("callable", InternalTraversal.dp_plain_dict), - ("value", InternalTraversal.dp_plain_obj), - ("literal_execute", InternalTraversal.dp_boolean), - ] - - key: str - type: TypeEngine[_T] - value: Optional[_T] - - _is_crud = False - _is_bind_parameter = True - _key_is_anon = False - - # bindparam implements its own _gen_cache_key() method however - # we check subclasses for this flag, else no cache key is generated - inherit_cache = True - - def __init__( - self, - key: Optional[str], - value: Any = _NoArg.NO_ARG, - type_: Optional[_TypeEngineArgument[_T]] = None, - unique: bool = False, - required: Union[bool, Literal[_NoArg.NO_ARG]] = _NoArg.NO_ARG, - quote: Optional[bool] = None, - callable_: Optional[Callable[[], Any]] = None, - expanding: bool = False, - isoutparam: bool = False, - literal_execute: bool = False, - _compared_to_operator: Optional[OperatorType] = None, - _compared_to_type: Optional[TypeEngine[Any]] = None, - _is_crud: bool = False, - ): - if required is _NoArg.NO_ARG: - required = value is _NoArg.NO_ARG and callable_ is None - if value is _NoArg.NO_ARG: - value = None - - if quote is not None: - key = quoted_name.construct(key, quote) - - if unique: - self.key = _anonymous_label.safe_construct( - id(self), - ( - key - if key is not None - and not isinstance(key, _anonymous_label) - else "param" - ), - sanitize_key=True, - ) - self._key_is_anon = True - elif key: - self.key = key - else: - self.key = _anonymous_label.safe_construct(id(self), "param") - self._key_is_anon = True - - # identifying key that won't change across - # clones, used to identify the bind's logical - # identity - self._identifying_key = self.key - - # key that was passed in the first place, used to - # generate new keys - self._orig_key = key or "param" - - self.unique = unique - self.value = value - self.callable = callable_ - self.isoutparam = isoutparam - self.required = required - - # indicate an "expanding" parameter; the compiler sets this - # automatically in the compiler _render_in_expr_w_bindparam method - # for an IN expression - self.expanding = expanding - - # this is another hint to help w/ expanding and is typically - # set in the compiler _render_in_expr_w_bindparam method for an - # IN expression - self.expand_op = None - - self.literal_execute = literal_execute - if _is_crud: - self._is_crud = True - - if type_ is None: - if expanding: - if value: - check_value = value[0] - else: - check_value = type_api._NO_VALUE_IN_LIST - else: - check_value = value - if _compared_to_type is not None: - self.type = _compared_to_type.coerce_compared_value( - _compared_to_operator, check_value - ) - else: - self.type = type_api._resolve_value_to_type(check_value) - elif isinstance(type_, type): - self.type = type_() - elif is_tuple_type(type_): - if value: - if expanding: - check_value = value[0] - else: - check_value = value - cast("BindParameter[typing_Tuple[Any, ...]]", self).type = ( - type_._resolve_values_to_types(check_value) - ) - else: - cast("BindParameter[typing_Tuple[Any, ...]]", self).type = ( - type_ - ) - else: - self.type = type_ - - def _with_value(self, value, maintain_key=False, required=NO_ARG): - """Return a copy of this :class:`.BindParameter` with the given value - set. - """ - cloned = self._clone(maintain_key=maintain_key) - cloned.value = value - cloned.callable = None - cloned.required = required if required is not NO_ARG else self.required - if cloned.type is type_api.NULLTYPE: - cloned.type = type_api._resolve_value_to_type(value) - return cloned - - @property - def effective_value(self) -> Optional[_T]: - """Return the value of this bound parameter, - taking into account if the ``callable`` parameter - was set. - - The ``callable`` value will be evaluated - and returned if present, else ``value``. - - """ - if self.callable: - # TODO: set up protocol for bind parameter callable - return self.callable() # type: ignore - else: - return self.value - - def render_literal_execute(self) -> BindParameter[_T]: - """Produce a copy of this bound parameter that will enable the - :paramref:`_sql.BindParameter.literal_execute` flag. - - The :paramref:`_sql.BindParameter.literal_execute` flag will - have the effect of the parameter rendered in the compiled SQL - string using ``[POSTCOMPILE]`` form, which is a special form that - is converted to be a rendering of the literal value of the parameter - at SQL execution time. The rationale is to support caching - of SQL statement strings that can embed per-statement literal values, - such as LIMIT and OFFSET parameters, in the final SQL string that - is passed to the DBAPI. Dialects in particular may want to use - this method within custom compilation schemes. - - .. versionadded:: 1.4.5 - - .. seealso:: - - :ref:`engine_thirdparty_caching` - - """ - c = ClauseElement._clone(self) - c.literal_execute = True - return c - - def _negate_in_binary(self, negated_op, original_op): - if self.expand_op is original_op: - bind = self._clone() - bind.expand_op = negated_op - return bind - else: - return self - - def _with_binary_element_type(self, type_): - c = ClauseElement._clone(self) - c.type = type_ - return c - - def _clone(self, maintain_key: bool = False, **kw: Any) -> Self: - c = ClauseElement._clone(self, **kw) - # ensure all the BindParameter objects stay in cloned set. - # in #7823, we changed "clone" so that a clone only keeps a reference - # to the "original" element, since for column correspondence, that's - # all we need. However, for BindParam, _cloned_set is used by - # the "cache key bind match" lookup, which means if any of those - # interim BindParameter objects became part of a cache key in the - # cache, we need it. So here, make sure all clones keep carrying - # forward. - c._cloned_set.update(self._cloned_set) - if not maintain_key and self.unique: - c.key = _anonymous_label.safe_construct( - id(c), c._orig_key or "param", sanitize_key=True - ) - return c - - def _gen_cache_key(self, anon_map, bindparams): - _gen_cache_ok = self.__class__.__dict__.get("inherit_cache", False) - - if not _gen_cache_ok: - if anon_map is not None: - anon_map[NO_CACHE] = True - return None - - id_, found = anon_map.get_anon(self) - if found: - return (id_, self.__class__) - - if bindparams is not None: - bindparams.append(self) - - return ( - id_, - self.__class__, - self.type._static_cache_key, - self.key % anon_map if self._key_is_anon else self.key, - self.literal_execute, - ) - - def _convert_to_unique(self): - if not self.unique: - self.unique = True - self.key = _anonymous_label.safe_construct( - id(self), self._orig_key or "param", sanitize_key=True - ) - - def __getstate__(self): - """execute a deferred value for serialization purposes.""" - - d = self.__dict__.copy() - v = self.value - if self.callable: - v = self.callable() - d["callable"] = None - d["value"] = v - return d - - def __setstate__(self, state): - if state.get("unique", False): - state["key"] = _anonymous_label.safe_construct( - id(self), state.get("_orig_key", "param"), sanitize_key=True - ) - self.__dict__.update(state) - - def __repr__(self): - return "%s(%r, %r, type_=%r)" % ( - self.__class__.__name__, - self.key, - self.value, - self.type, - ) - - -class TypeClause(DQLDMLClauseElement): - """Handle a type keyword in a SQL statement. - - Used by the ``Case`` statement. - - """ - - __visit_name__ = "typeclause" - - _traverse_internals: _TraverseInternalsType = [ - ("type", InternalTraversal.dp_type) - ] - - def __init__(self, type_): - self.type = type_ - - -class TextClause( - roles.DDLConstraintColumnRole, - roles.DDLExpressionRole, - roles.StatementOptionRole, - roles.WhereHavingRole, - roles.OrderByRole, - roles.FromClauseRole, - roles.SelectStatementRole, - roles.InElementRole, - Generative, - Executable, - DQLDMLClauseElement, - roles.BinaryElementRole[Any], - inspection.Inspectable["TextClause"], -): - """Represent a literal SQL text fragment. - - E.g.:: - - from sqlalchemy import text - - t = text("SELECT * FROM users") - result = connection.execute(t) - - - The :class:`_expression.TextClause` construct is produced using the - :func:`_expression.text` - function; see that function for full documentation. - - .. seealso:: - - :func:`_expression.text` - - """ - - __visit_name__ = "textclause" - - _traverse_internals: _TraverseInternalsType = [ - ("_bindparams", InternalTraversal.dp_string_clauseelement_dict), - ("text", InternalTraversal.dp_string), - ] - - _is_text_clause = True - - _is_textual = True - - _bind_params_regex = re.compile(r"(?<![:\w\x5c]):(\w+)(?!:)", re.UNICODE) - _is_implicitly_boolean = False - - _render_label_in_columns_clause = False - - _omit_from_statements = False - - _is_collection_aggregate = False - - @property - def _hide_froms(self) -> Iterable[FromClause]: - return () - - def __and__(self, other): - # support use in select.where(), query.filter() - return and_(self, other) - - @property - def _select_iterable(self) -> _SelectIterable: - return (self,) - - # help in those cases where text() is - # interpreted in a column expression situation - key: Optional[str] = None - _label: Optional[str] = None - - _allow_label_resolve = False - - @property - def _is_star(self): - return self.text == "*" - - def __init__(self, text: str): - self._bindparams: Dict[str, BindParameter[Any]] = {} - - def repl(m): - self._bindparams[m.group(1)] = BindParameter(m.group(1)) - return ":%s" % m.group(1) - - # scan the string and search for bind parameter names, add them - # to the list of bindparams - self.text = self._bind_params_regex.sub(repl, text) - - @_generative - def bindparams( - self, - *binds: BindParameter[Any], - **names_to_values: Any, - ) -> Self: - """Establish the values and/or types of bound parameters within - this :class:`_expression.TextClause` construct. - - Given a text construct such as:: - - from sqlalchemy import text - stmt = text("SELECT id, name FROM user WHERE name=:name " - "AND timestamp=:timestamp") - - the :meth:`_expression.TextClause.bindparams` - method can be used to establish - the initial value of ``:name`` and ``:timestamp``, - using simple keyword arguments:: - - stmt = stmt.bindparams(name='jack', - timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)) - - Where above, new :class:`.BindParameter` objects - will be generated with the names ``name`` and ``timestamp``, and - values of ``jack`` and ``datetime.datetime(2012, 10, 8, 15, 12, 5)``, - respectively. The types will be - inferred from the values given, in this case :class:`.String` and - :class:`.DateTime`. - - When specific typing behavior is needed, the positional ``*binds`` - argument can be used in which to specify :func:`.bindparam` constructs - directly. These constructs must include at least the ``key`` - argument, then an optional value and type:: - - from sqlalchemy import bindparam - stmt = stmt.bindparams( - bindparam('name', value='jack', type_=String), - bindparam('timestamp', type_=DateTime) - ) - - Above, we specified the type of :class:`.DateTime` for the - ``timestamp`` bind, and the type of :class:`.String` for the ``name`` - bind. In the case of ``name`` we also set the default value of - ``"jack"``. - - Additional bound parameters can be supplied at statement execution - time, e.g.:: - - result = connection.execute(stmt, - timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5)) - - The :meth:`_expression.TextClause.bindparams` - method can be called repeatedly, - where it will re-use existing :class:`.BindParameter` objects to add - new information. For example, we can call - :meth:`_expression.TextClause.bindparams` - first with typing information, and a - second time with value information, and it will be combined:: - - stmt = text("SELECT id, name FROM user WHERE name=:name " - "AND timestamp=:timestamp") - stmt = stmt.bindparams( - bindparam('name', type_=String), - bindparam('timestamp', type_=DateTime) - ) - stmt = stmt.bindparams( - name='jack', - timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5) - ) - - The :meth:`_expression.TextClause.bindparams` - method also supports the concept of - **unique** bound parameters. These are parameters that are - "uniquified" on name at statement compilation time, so that multiple - :func:`_expression.text` - constructs may be combined together without the names - conflicting. To use this feature, specify the - :paramref:`.BindParameter.unique` flag on each :func:`.bindparam` - object:: - - stmt1 = text("select id from table where name=:name").bindparams( - bindparam("name", value='name1', unique=True) - ) - stmt2 = text("select id from table where name=:name").bindparams( - bindparam("name", value='name2', unique=True) - ) - - union = union_all( - stmt1.columns(column("id")), - stmt2.columns(column("id")) - ) - - The above statement will render as:: - - select id from table where name=:name_1 - UNION ALL select id from table where name=:name_2 - - .. versionadded:: 1.3.11 Added support for the - :paramref:`.BindParameter.unique` flag to work with - :func:`_expression.text` - constructs. - - """ - self._bindparams = new_params = self._bindparams.copy() - - for bind in binds: - try: - # the regex used for text() currently will not match - # a unique/anonymous key in any case, so use the _orig_key - # so that a text() construct can support unique parameters - existing = new_params[bind._orig_key] - except KeyError as err: - raise exc.ArgumentError( - "This text() construct doesn't define a " - "bound parameter named %r" % bind._orig_key - ) from err - else: - new_params[existing._orig_key] = bind - - for key, value in names_to_values.items(): - try: - existing = new_params[key] - except KeyError as err: - raise exc.ArgumentError( - "This text() construct doesn't define a " - "bound parameter named %r" % key - ) from err - else: - new_params[key] = existing._with_value(value, required=False) - return self - - @util.preload_module("sqlalchemy.sql.selectable") - def columns( - self, *cols: _ColumnExpressionArgument[Any], **types: TypeEngine[Any] - ) -> TextualSelect: - r"""Turn this :class:`_expression.TextClause` object into a - :class:`_expression.TextualSelect` - object that serves the same role as a SELECT - statement. - - The :class:`_expression.TextualSelect` is part of the - :class:`_expression.SelectBase` - hierarchy and can be embedded into another statement by using the - :meth:`_expression.TextualSelect.subquery` method to produce a - :class:`.Subquery` - object, which can then be SELECTed from. - - This function essentially bridges the gap between an entirely - textual SELECT statement and the SQL expression language concept - of a "selectable":: - - from sqlalchemy.sql import column, text - - stmt = text("SELECT id, name FROM some_table") - stmt = stmt.columns(column('id'), column('name')).subquery('st') - - stmt = select(mytable).\ - select_from( - mytable.join(stmt, mytable.c.name == stmt.c.name) - ).where(stmt.c.id > 5) - - Above, we pass a series of :func:`_expression.column` elements to the - :meth:`_expression.TextClause.columns` method positionally. These - :func:`_expression.column` - elements now become first class elements upon the - :attr:`_expression.TextualSelect.selected_columns` column collection, - which then - become part of the :attr:`.Subquery.c` collection after - :meth:`_expression.TextualSelect.subquery` is invoked. - - The column expressions we pass to - :meth:`_expression.TextClause.columns` may - also be typed; when we do so, these :class:`.TypeEngine` objects become - the effective return type of the column, so that SQLAlchemy's - result-set-processing systems may be used on the return values. - This is often needed for types such as date or boolean types, as well - as for unicode processing on some dialect configurations:: - - stmt = text("SELECT id, name, timestamp FROM some_table") - stmt = stmt.columns( - column('id', Integer), - column('name', Unicode), - column('timestamp', DateTime) - ) - - for id, name, timestamp in connection.execute(stmt): - print(id, name, timestamp) - - As a shortcut to the above syntax, keyword arguments referring to - types alone may be used, if only type conversion is needed:: - - stmt = text("SELECT id, name, timestamp FROM some_table") - stmt = stmt.columns( - id=Integer, - name=Unicode, - timestamp=DateTime - ) - - for id, name, timestamp in connection.execute(stmt): - print(id, name, timestamp) - - The positional form of :meth:`_expression.TextClause.columns` - also provides the - unique feature of **positional column targeting**, which is - particularly useful when using the ORM with complex textual queries. If - we specify the columns from our model to - :meth:`_expression.TextClause.columns`, - the result set will match to those columns positionally, meaning the - name or origin of the column in the textual SQL doesn't matter:: - - stmt = text("SELECT users.id, addresses.id, users.id, " - "users.name, addresses.email_address AS email " - "FROM users JOIN addresses ON users.id=addresses.user_id " - "WHERE users.id = 1").columns( - User.id, - Address.id, - Address.user_id, - User.name, - Address.email_address - ) - - query = session.query(User).from_statement(stmt).options( - contains_eager(User.addresses)) - - The :meth:`_expression.TextClause.columns` method provides a direct - route to calling :meth:`_expression.FromClause.subquery` as well as - :meth:`_expression.SelectBase.cte` - against a textual SELECT statement:: - - stmt = stmt.columns(id=Integer, name=String).cte('st') - - stmt = select(sometable).where(sometable.c.id == stmt.c.id) - - :param \*cols: A series of :class:`_expression.ColumnElement` objects, - typically - :class:`_schema.Column` objects from a :class:`_schema.Table` - or ORM level - column-mapped attributes, representing a set of columns that this - textual string will SELECT from. - - :param \**types: A mapping of string names to :class:`.TypeEngine` - type objects indicating the datatypes to use for names that are - SELECTed from the textual string. Prefer to use the ``*cols`` - argument as it also indicates positional ordering. - - """ - selectable = util.preloaded.sql_selectable - - input_cols: List[NamedColumn[Any]] = [ - coercions.expect(roles.LabeledColumnExprRole, col) for col in cols - ] - - positional_input_cols = [ - ( - ColumnClause(col.key, types.pop(col.key)) - if col.key in types - else col - ) - for col in input_cols - ] - keyed_input_cols: List[NamedColumn[Any]] = [ - ColumnClause(key, type_) for key, type_ in types.items() - ] - - elem = selectable.TextualSelect.__new__(selectable.TextualSelect) - elem._init( - self, - positional_input_cols + keyed_input_cols, - positional=bool(positional_input_cols) and not keyed_input_cols, - ) - return elem - - @property - def type(self) -> TypeEngine[Any]: - return type_api.NULLTYPE - - @property - def comparator(self): - # TODO: this seems wrong, it seems like we might not - # be using this method. - return self.type.comparator_factory(self) # type: ignore - - def self_group(self, against=None): - if against is operators.in_op: - return Grouping(self) - else: - return self - - -class Null(SingletonConstant, roles.ConstExprRole[None], ColumnElement[None]): - """Represent the NULL keyword in a SQL statement. - - :class:`.Null` is accessed as a constant via the - :func:`.null` function. - - """ - - __visit_name__ = "null" - - _traverse_internals: _TraverseInternalsType = [] - _singleton: Null - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - return type_api.NULLTYPE - - @classmethod - def _instance(cls) -> Null: - """Return a constant :class:`.Null` construct.""" - - return Null._singleton - - -Null._create_singleton() - - -class False_( - SingletonConstant, roles.ConstExprRole[bool], ColumnElement[bool] -): - """Represent the ``false`` keyword, or equivalent, in a SQL statement. - - :class:`.False_` is accessed as a constant via the - :func:`.false` function. - - """ - - __visit_name__ = "false" - _traverse_internals: _TraverseInternalsType = [] - _singleton: False_ - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - return type_api.BOOLEANTYPE - - def _negate(self) -> True_: - return True_._singleton - - @classmethod - def _instance(cls) -> False_: - return False_._singleton - - -False_._create_singleton() - - -class True_(SingletonConstant, roles.ConstExprRole[bool], ColumnElement[bool]): - """Represent the ``true`` keyword, or equivalent, in a SQL statement. - - :class:`.True_` is accessed as a constant via the - :func:`.true` function. - - """ - - __visit_name__ = "true" - - _traverse_internals: _TraverseInternalsType = [] - _singleton: True_ - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - return type_api.BOOLEANTYPE - - def _negate(self) -> False_: - return False_._singleton - - @classmethod - def _ifnone( - cls, other: Optional[ColumnElement[Any]] - ) -> ColumnElement[Any]: - if other is None: - return cls._instance() - else: - return other - - @classmethod - def _instance(cls) -> True_: - return True_._singleton - - -True_._create_singleton() - - -class ClauseList( - roles.InElementRole, - roles.OrderByRole, - roles.ColumnsClauseRole, - roles.DMLColumnRole, - DQLDMLClauseElement, -): - """Describe a list of clauses, separated by an operator. - - By default, is comma-separated, such as a column listing. - - """ - - __visit_name__ = "clauselist" - - # this is used only by the ORM in a legacy use case for - # composite attributes - _is_clause_list = True - - _traverse_internals: _TraverseInternalsType = [ - ("clauses", InternalTraversal.dp_clauseelement_list), - ("operator", InternalTraversal.dp_operator), - ] - - clauses: List[ColumnElement[Any]] - - def __init__( - self, - *clauses: _ColumnExpressionArgument[Any], - operator: OperatorType = operators.comma_op, - group: bool = True, - group_contents: bool = True, - _literal_as_text_role: Type[roles.SQLRole] = roles.WhereHavingRole, - ): - self.operator = operator - self.group = group - self.group_contents = group_contents - clauses_iterator: Iterable[_ColumnExpressionArgument[Any]] = clauses - text_converter_role: Type[roles.SQLRole] = _literal_as_text_role - self._text_converter_role = text_converter_role - - if self.group_contents: - self.clauses = [ - coercions.expect( - text_converter_role, clause, apply_propagate_attrs=self - ).self_group(against=self.operator) - for clause in clauses_iterator - ] - else: - self.clauses = [ - coercions.expect( - text_converter_role, clause, apply_propagate_attrs=self - ) - for clause in clauses_iterator - ] - self._is_implicitly_boolean = operators.is_boolean(self.operator) - - @classmethod - def _construct_raw( - cls, - operator: OperatorType, - clauses: Optional[Sequence[ColumnElement[Any]]] = None, - ) -> ClauseList: - self = cls.__new__(cls) - self.clauses = list(clauses) if clauses else [] - self.group = True - self.operator = operator - self.group_contents = True - self._is_implicitly_boolean = False - return self - - def __iter__(self) -> Iterator[ColumnElement[Any]]: - return iter(self.clauses) - - def __len__(self) -> int: - return len(self.clauses) - - @property - def _select_iterable(self) -> _SelectIterable: - return itertools.chain.from_iterable( - [elem._select_iterable for elem in self.clauses] - ) - - def append(self, clause): - if self.group_contents: - self.clauses.append( - coercions.expect(self._text_converter_role, clause).self_group( - against=self.operator - ) - ) - else: - self.clauses.append( - coercions.expect(self._text_converter_role, clause) - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list(itertools.chain(*[c._from_objects for c in self.clauses])) - - def self_group(self, against=None): - if self.group and operators.is_precedent(self.operator, against): - return Grouping(self) - else: - return self - - -class OperatorExpression(ColumnElement[_T]): - """base for expressions that contain an operator and operands - - .. versionadded:: 2.0 - - """ - - operator: OperatorType - type: TypeEngine[_T] - - group: bool = True - - @property - def is_comparison(self): - return operators.is_comparison(self.operator) - - def self_group(self, against=None): - if ( - self.group - and operators.is_precedent(self.operator, against) - or ( - # a negate against a non-boolean operator - # doesn't make too much sense but we should - # group for that - against is operators.inv - and not operators.is_boolean(self.operator) - ) - ): - return Grouping(self) - else: - return self - - @property - def _flattened_operator_clauses( - self, - ) -> typing_Tuple[ColumnElement[Any], ...]: - raise NotImplementedError() - - @classmethod - def _construct_for_op( - cls, - left: ColumnElement[Any], - right: ColumnElement[Any], - op: OperatorType, - *, - type_: TypeEngine[_T], - negate: Optional[OperatorType] = None, - modifiers: Optional[Mapping[str, Any]] = None, - ) -> OperatorExpression[_T]: - if operators.is_associative(op): - assert ( - negate is None - ), f"negate not supported for associative operator {op}" - - multi = False - if getattr( - left, "operator", None - ) is op and type_._compare_type_affinity(left.type): - multi = True - left_flattened = left._flattened_operator_clauses - else: - left_flattened = (left,) - - if getattr( - right, "operator", None - ) is op and type_._compare_type_affinity(right.type): - multi = True - right_flattened = right._flattened_operator_clauses - else: - right_flattened = (right,) - - if multi: - return ExpressionClauseList._construct_for_list( - op, - type_, - *(left_flattened + right_flattened), - ) - - if right._is_collection_aggregate: - negate = None - - return BinaryExpression( - left, right, op, type_=type_, negate=negate, modifiers=modifiers - ) - - -class ExpressionClauseList(OperatorExpression[_T]): - """Describe a list of clauses, separated by an operator, - in a column expression context. - - :class:`.ExpressionClauseList` differs from :class:`.ClauseList` in that - it represents a column-oriented DQL expression only, not an open ended - list of anything comma separated. - - .. versionadded:: 2.0 - - """ - - __visit_name__ = "expression_clauselist" - - _traverse_internals: _TraverseInternalsType = [ - ("clauses", InternalTraversal.dp_clauseelement_tuple), - ("operator", InternalTraversal.dp_operator), - ] - - clauses: typing_Tuple[ColumnElement[Any], ...] - - group: bool - - def __init__( - self, - operator: OperatorType, - *clauses: _ColumnExpressionArgument[Any], - type_: Optional[_TypeEngineArgument[_T]] = None, - ): - self.operator = operator - - self.clauses = tuple( - coercions.expect( - roles.ExpressionElementRole, clause, apply_propagate_attrs=self - ) - for clause in clauses - ) - self._is_implicitly_boolean = operators.is_boolean(self.operator) - self.type = type_api.to_instance(type_) # type: ignore - - @property - def _flattened_operator_clauses( - self, - ) -> typing_Tuple[ColumnElement[Any], ...]: - return self.clauses - - def __iter__(self) -> Iterator[ColumnElement[Any]]: - return iter(self.clauses) - - def __len__(self) -> int: - return len(self.clauses) - - @property - def _select_iterable(self) -> _SelectIterable: - return (self,) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list(itertools.chain(*[c._from_objects for c in self.clauses])) - - def _append_inplace(self, clause: ColumnElement[Any]) -> None: - self.clauses += (clause,) - - @classmethod - def _construct_for_list( - cls, - operator: OperatorType, - type_: TypeEngine[_T], - *clauses: ColumnElement[Any], - group: bool = True, - ) -> ExpressionClauseList[_T]: - self = cls.__new__(cls) - self.group = group - if group: - self.clauses = tuple( - c.self_group(against=operator) for c in clauses - ) - else: - self.clauses = clauses - self.operator = operator - self.type = type_ - return self - - def _negate(self) -> Any: - grouped = self.self_group(against=operators.inv) - assert isinstance(grouped, ColumnElement) - return UnaryExpression( - grouped, operator=operators.inv, wraps_column_expression=True - ) - - -class BooleanClauseList(ExpressionClauseList[bool]): - __visit_name__ = "expression_clauselist" - inherit_cache = True - - def __init__(self, *arg, **kw): - raise NotImplementedError( - "BooleanClauseList has a private constructor" - ) - - @classmethod - def _process_clauses_for_boolean( - cls, - operator: OperatorType, - continue_on: Any, - skip_on: Any, - clauses: Iterable[ColumnElement[Any]], - ) -> typing_Tuple[int, List[ColumnElement[Any]]]: - has_continue_on = None - - convert_clauses = [] - - against = operators._asbool - lcc = 0 - - for clause in clauses: - if clause is continue_on: - # instance of continue_on, like and_(x, y, True, z), store it - # if we didn't find one already, we will use it if there - # are no other expressions here. - has_continue_on = clause - elif clause is skip_on: - # instance of skip_on, e.g. and_(x, y, False, z), cancels - # the rest out - convert_clauses = [clause] - lcc = 1 - break - else: - if not lcc: - lcc = 1 - else: - against = operator - # technically this would be len(convert_clauses) + 1 - # however this only needs to indicate "greater than one" - lcc = 2 - convert_clauses.append(clause) - - if not convert_clauses and has_continue_on is not None: - convert_clauses = [has_continue_on] - lcc = 1 - - return lcc, [c.self_group(against=against) for c in convert_clauses] - - @classmethod - def _construct( - cls, - operator: OperatorType, - continue_on: Any, - skip_on: Any, - initial_clause: Any = _NoArg.NO_ARG, - *clauses: Any, - **kw: Any, - ) -> ColumnElement[Any]: - if initial_clause is _NoArg.NO_ARG: - # no elements period. deprecated use case. return an empty - # ClauseList construct that generates nothing unless it has - # elements added to it. - name = operator.__name__ - - util.warn_deprecated( - f"Invoking {name}() without arguments is deprecated, and " - f"will be disallowed in a future release. For an empty " - f"""{name}() construct, use '{name}({ - 'true()' if continue_on is True_._singleton else 'false()' - }, *args)' """ - f"""or '{name}({ - 'True' if continue_on is True_._singleton else 'False' - }, *args)'.""", - version="1.4", - ) - return cls._construct_raw(operator) - - lcc, convert_clauses = cls._process_clauses_for_boolean( - operator, - continue_on, - skip_on, - [ - coercions.expect(roles.WhereHavingRole, clause) - for clause in util.coerce_generator_arg( - (initial_clause,) + clauses - ) - ], - ) - - if lcc > 1: - # multiple elements. Return regular BooleanClauseList - # which will link elements against the operator. - - flattened_clauses = itertools.chain.from_iterable( - ( - (c for c in to_flat._flattened_operator_clauses) - if getattr(to_flat, "operator", None) is operator - else (to_flat,) - ) - for to_flat in convert_clauses - ) - - return cls._construct_raw(operator, flattened_clauses) # type: ignore # noqa: E501 - else: - assert lcc - # just one element. return it as a single boolean element, - # not a list and discard the operator. - return convert_clauses[0] - - @classmethod - def _construct_for_whereclause( - cls, clauses: Iterable[ColumnElement[Any]] - ) -> Optional[ColumnElement[bool]]: - operator, continue_on, skip_on = ( - operators.and_, - True_._singleton, - False_._singleton, - ) - - lcc, convert_clauses = cls._process_clauses_for_boolean( - operator, - continue_on, - skip_on, - clauses, # these are assumed to be coerced already - ) - - if lcc > 1: - # multiple elements. Return regular BooleanClauseList - # which will link elements against the operator. - return cls._construct_raw(operator, convert_clauses) - elif lcc == 1: - # just one element. return it as a single boolean element, - # not a list and discard the operator. - return convert_clauses[0] - else: - return None - - @classmethod - def _construct_raw( - cls, - operator: OperatorType, - clauses: Optional[Sequence[ColumnElement[Any]]] = None, - ) -> BooleanClauseList: - self = cls.__new__(cls) - self.clauses = tuple(clauses) if clauses else () - self.group = True - self.operator = operator - self.type = type_api.BOOLEANTYPE - self._is_implicitly_boolean = True - return self - - @classmethod - def and_( - cls, - initial_clause: Union[ - Literal[True], _ColumnExpressionArgument[bool], _NoArg - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionArgument[bool], - ) -> ColumnElement[bool]: - r"""Produce a conjunction of expressions joined by ``AND``. - - See :func:`_sql.and_` for full documentation. - """ - return cls._construct( - operators.and_, - True_._singleton, - False_._singleton, - initial_clause, - *clauses, - ) - - @classmethod - def or_( - cls, - initial_clause: Union[ - Literal[False], _ColumnExpressionArgument[bool], _NoArg - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionArgument[bool], - ) -> ColumnElement[bool]: - """Produce a conjunction of expressions joined by ``OR``. - - See :func:`_sql.or_` for full documentation. - """ - return cls._construct( - operators.or_, - False_._singleton, - True_._singleton, - initial_clause, - *clauses, - ) - - @property - def _select_iterable(self) -> _SelectIterable: - return (self,) - - def self_group(self, against=None): - if not self.clauses: - return self - else: - return super().self_group(against=against) - - -and_ = BooleanClauseList.and_ -or_ = BooleanClauseList.or_ - - -class Tuple(ClauseList, ColumnElement[typing_Tuple[Any, ...]]): - """Represent a SQL tuple.""" - - __visit_name__ = "tuple" - - _traverse_internals: _TraverseInternalsType = ( - ClauseList._traverse_internals + [] - ) - - type: TupleType - - @util.preload_module("sqlalchemy.sql.sqltypes") - def __init__( - self, - *clauses: _ColumnExpressionArgument[Any], - types: Optional[Sequence[_TypeEngineArgument[Any]]] = None, - ): - sqltypes = util.preloaded.sql_sqltypes - - if types is None: - init_clauses: List[ColumnElement[Any]] = [ - coercions.expect(roles.ExpressionElementRole, c) - for c in clauses - ] - else: - if len(types) != len(clauses): - raise exc.ArgumentError( - "Wrong number of elements for %d-tuple: %r " - % (len(types), clauses) - ) - init_clauses = [ - coercions.expect( - roles.ExpressionElementRole, - c, - type_=typ if not typ._isnull else None, - ) - for typ, c in zip(types, clauses) - ] - - self.type = sqltypes.TupleType(*[arg.type for arg in init_clauses]) - super().__init__(*init_clauses) - - @property - def _select_iterable(self) -> _SelectIterable: - return (self,) - - def _bind_param(self, operator, obj, type_=None, expanding=False): - if expanding: - return BindParameter( - None, - value=obj, - _compared_to_operator=operator, - unique=True, - expanding=True, - type_=type_, - _compared_to_type=self.type, - ) - else: - return Tuple( - *[ - BindParameter( - None, - o, - _compared_to_operator=operator, - _compared_to_type=compared_to_type, - unique=True, - type_=type_, - ) - for o, compared_to_type in zip(obj, self.type.types) - ] - ) - - def self_group(self, against=None): - # Tuple is parenthesized by definition. - return self - - -class Case(ColumnElement[_T]): - """Represent a ``CASE`` expression. - - :class:`.Case` is produced using the :func:`.case` factory function, - as in:: - - from sqlalchemy import case - - stmt = select(users_table).\ - where( - case( - (users_table.c.name == 'wendy', 'W'), - (users_table.c.name == 'jack', 'J'), - else_='E' - ) - ) - - Details on :class:`.Case` usage is at :func:`.case`. - - .. seealso:: - - :func:`.case` - - """ - - __visit_name__ = "case" - - _traverse_internals: _TraverseInternalsType = [ - ("value", InternalTraversal.dp_clauseelement), - ("whens", InternalTraversal.dp_clauseelement_tuples), - ("else_", InternalTraversal.dp_clauseelement), - ] - - # for case(), the type is derived from the whens. so for the moment - # users would have to cast() the case to get a specific type - - whens: List[typing_Tuple[ColumnElement[bool], ColumnElement[_T]]] - else_: Optional[ColumnElement[_T]] - value: Optional[ColumnElement[Any]] - - def __init__( - self, - *whens: Union[ - typing_Tuple[_ColumnExpressionArgument[bool], Any], - Mapping[Any, Any], - ], - value: Optional[Any] = None, - else_: Optional[Any] = None, - ): - new_whens: Iterable[Any] = coercions._expression_collection_was_a_list( - "whens", "case", whens - ) - try: - new_whens = util.dictlike_iteritems(new_whens) - except TypeError: - pass - - self.whens = [ - ( - coercions.expect( - roles.ExpressionElementRole, - c, - apply_propagate_attrs=self, - ).self_group(), - coercions.expect(roles.ExpressionElementRole, r), - ) - for (c, r) in new_whens - ] - - if value is None: - self.value = None - else: - self.value = coercions.expect(roles.ExpressionElementRole, value) - - if else_ is not None: - self.else_ = coercions.expect(roles.ExpressionElementRole, else_) - else: - self.else_ = None - - type_ = next( - ( - then.type - # Iterate `whens` in reverse to match previous behaviour - # where type of final element took priority - for *_, then in reversed(self.whens) - if not then.type._isnull - ), - self.else_.type if self.else_ is not None else type_api.NULLTYPE, - ) - self.type = cast(_T, type_) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list( - itertools.chain(*[x._from_objects for x in self.get_children()]) - ) - - -class Cast(WrapsColumnExpression[_T]): - """Represent a ``CAST`` expression. - - :class:`.Cast` is produced using the :func:`.cast` factory function, - as in:: - - from sqlalchemy import cast, Numeric - - stmt = select(cast(product_table.c.unit_price, Numeric(10, 4))) - - Details on :class:`.Cast` usage is at :func:`.cast`. - - .. seealso:: - - :ref:`tutorial_casts` - - :func:`.cast` - - :func:`.try_cast` - - :func:`.type_coerce` - an alternative to CAST that coerces the type - on the Python side only, which is often sufficient to generate the - correct SQL and data coercion. - - """ - - __visit_name__ = "cast" - - _traverse_internals: _TraverseInternalsType = [ - ("clause", InternalTraversal.dp_clauseelement), - ("type", InternalTraversal.dp_type), - ] - - clause: ColumnElement[Any] - type: TypeEngine[_T] - typeclause: TypeClause - - def __init__( - self, - expression: _ColumnExpressionArgument[Any], - type_: _TypeEngineArgument[_T], - ): - self.type = type_api.to_instance(type_) - self.clause = coercions.expect( - roles.ExpressionElementRole, - expression, - type_=self.type, - apply_propagate_attrs=self, - ) - self.typeclause = TypeClause(self.type) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.clause._from_objects - - @property - def wrapped_column_expression(self): - return self.clause - - -class TryCast(Cast[_T]): - """Represent a TRY_CAST expression. - - Details on :class:`.TryCast` usage is at :func:`.try_cast`. - - .. seealso:: - - :func:`.try_cast` - - :ref:`tutorial_casts` - """ - - __visit_name__ = "try_cast" - inherit_cache = True - - -class TypeCoerce(WrapsColumnExpression[_T]): - """Represent a Python-side type-coercion wrapper. - - :class:`.TypeCoerce` supplies the :func:`_expression.type_coerce` - function; see that function for usage details. - - .. seealso:: - - :func:`_expression.type_coerce` - - :func:`.cast` - - """ - - __visit_name__ = "type_coerce" - - _traverse_internals: _TraverseInternalsType = [ - ("clause", InternalTraversal.dp_clauseelement), - ("type", InternalTraversal.dp_type), - ] - - clause: ColumnElement[Any] - type: TypeEngine[_T] - - def __init__( - self, - expression: _ColumnExpressionArgument[Any], - type_: _TypeEngineArgument[_T], - ): - self.type = type_api.to_instance(type_) - self.clause = coercions.expect( - roles.ExpressionElementRole, - expression, - type_=self.type, - apply_propagate_attrs=self, - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.clause._from_objects - - @HasMemoized.memoized_attribute - def typed_expression(self): - if isinstance(self.clause, BindParameter): - bp = self.clause._clone() - bp.type = self.type - return bp - else: - return self.clause - - @property - def wrapped_column_expression(self): - return self.clause - - def self_group(self, against=None): - grouped = self.clause.self_group(against=against) - if grouped is not self.clause: - return TypeCoerce(grouped, self.type) - else: - return self - - -class Extract(ColumnElement[int]): - """Represent a SQL EXTRACT clause, ``extract(field FROM expr)``.""" - - __visit_name__ = "extract" - - _traverse_internals: _TraverseInternalsType = [ - ("expr", InternalTraversal.dp_clauseelement), - ("field", InternalTraversal.dp_string), - ] - - expr: ColumnElement[Any] - field: str - - def __init__(self, field: str, expr: _ColumnExpressionArgument[Any]): - self.type = type_api.INTEGERTYPE - self.field = field - self.expr = coercions.expect(roles.ExpressionElementRole, expr) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.expr._from_objects - - -class _label_reference(ColumnElement[_T]): - """Wrap a column expression as it appears in a 'reference' context. - - This expression is any that includes an _order_by_label_element, - which is a Label, or a DESC / ASC construct wrapping a Label. - - The production of _label_reference() should occur when an expression - is added to this context; this includes the ORDER BY or GROUP BY of a - SELECT statement, as well as a few other places, such as the ORDER BY - within an OVER clause. - - """ - - __visit_name__ = "label_reference" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement) - ] - - element: ColumnElement[_T] - - def __init__(self, element: ColumnElement[_T]): - self.element = element - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [] - - -class _textual_label_reference(ColumnElement[Any]): - __visit_name__ = "textual_label_reference" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_string) - ] - - def __init__(self, element: str): - self.element = element - - @util.memoized_property - def _text_clause(self) -> TextClause: - return TextClause(self.element) - - -class UnaryExpression(ColumnElement[_T]): - """Define a 'unary' expression. - - A unary expression has a single column expression - and an operator. The operator can be placed on the left - (where it is called the 'operator') or right (where it is called the - 'modifier') of the column expression. - - :class:`.UnaryExpression` is the basis for several unary operators - including those used by :func:`.desc`, :func:`.asc`, :func:`.distinct`, - :func:`.nulls_first` and :func:`.nulls_last`. - - """ - - __visit_name__ = "unary" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("operator", InternalTraversal.dp_operator), - ("modifier", InternalTraversal.dp_operator), - ] - - element: ClauseElement - - def __init__( - self, - element: ColumnElement[Any], - operator: Optional[OperatorType] = None, - modifier: Optional[OperatorType] = None, - type_: Optional[_TypeEngineArgument[_T]] = None, - wraps_column_expression: bool = False, - ): - self.operator = operator - self.modifier = modifier - self._propagate_attrs = element._propagate_attrs - self.element = element.self_group( - against=self.operator or self.modifier - ) - - # if type is None, we get NULLTYPE, which is our _T. But I don't - # know how to get the overloads to express that correctly - self.type = type_api.to_instance(type_) # type: ignore - - self.wraps_column_expression = wraps_column_expression - - @classmethod - def _create_nulls_first( - cls, - column: _ColumnExpressionArgument[_T], - ) -> UnaryExpression[_T]: - return UnaryExpression( - coercions.expect(roles.ByOfRole, column), - modifier=operators.nulls_first_op, - wraps_column_expression=False, - ) - - @classmethod - def _create_nulls_last( - cls, - column: _ColumnExpressionArgument[_T], - ) -> UnaryExpression[_T]: - return UnaryExpression( - coercions.expect(roles.ByOfRole, column), - modifier=operators.nulls_last_op, - wraps_column_expression=False, - ) - - @classmethod - def _create_desc( - cls, column: _ColumnExpressionOrStrLabelArgument[_T] - ) -> UnaryExpression[_T]: - return UnaryExpression( - coercions.expect(roles.ByOfRole, column), - modifier=operators.desc_op, - wraps_column_expression=False, - ) - - @classmethod - def _create_asc( - cls, - column: _ColumnExpressionOrStrLabelArgument[_T], - ) -> UnaryExpression[_T]: - return UnaryExpression( - coercions.expect(roles.ByOfRole, column), - modifier=operators.asc_op, - wraps_column_expression=False, - ) - - @classmethod - def _create_distinct( - cls, - expr: _ColumnExpressionArgument[_T], - ) -> UnaryExpression[_T]: - col_expr: ColumnElement[_T] = coercions.expect( - roles.ExpressionElementRole, expr - ) - return UnaryExpression( - col_expr, - operator=operators.distinct_op, - type_=col_expr.type, - wraps_column_expression=False, - ) - - @classmethod - def _create_bitwise_not( - cls, - expr: _ColumnExpressionArgument[_T], - ) -> UnaryExpression[_T]: - col_expr: ColumnElement[_T] = coercions.expect( - roles.ExpressionElementRole, expr - ) - return UnaryExpression( - col_expr, - operator=operators.bitwise_not_op, - type_=col_expr.type, - wraps_column_expression=False, - ) - - @property - def _order_by_label_element(self) -> Optional[Label[Any]]: - if self.modifier in (operators.desc_op, operators.asc_op): - return self.element._order_by_label_element - else: - return None - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - def _negate(self): - if self.type._type_affinity is type_api.BOOLEANTYPE._type_affinity: - return UnaryExpression( - self.self_group(against=operators.inv), - operator=operators.inv, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=self.wraps_column_expression, - ) - else: - return ClauseElement._negate(self) - - def self_group(self, against=None): - if self.operator and operators.is_precedent(self.operator, against): - return Grouping(self) - else: - return self - - -class CollectionAggregate(UnaryExpression[_T]): - """Forms the basis for right-hand collection operator modifiers - ANY and ALL. - - The ANY and ALL keywords are available in different ways on different - backends. On PostgreSQL, they only work for an ARRAY type. On - MySQL, they only work for subqueries. - - """ - - inherit_cache = True - _is_collection_aggregate = True - - @classmethod - def _create_any( - cls, expr: _ColumnExpressionArgument[_T] - ) -> CollectionAggregate[bool]: - col_expr: ColumnElement[_T] = coercions.expect( - roles.ExpressionElementRole, - expr, - ) - col_expr = col_expr.self_group() - return CollectionAggregate( - col_expr, - operator=operators.any_op, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=False, - ) - - @classmethod - def _create_all( - cls, expr: _ColumnExpressionArgument[_T] - ) -> CollectionAggregate[bool]: - col_expr: ColumnElement[_T] = coercions.expect( - roles.ExpressionElementRole, - expr, - ) - col_expr = col_expr.self_group() - return CollectionAggregate( - col_expr, - operator=operators.all_op, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=False, - ) - - # operate and reverse_operate are hardwired to - # dispatch onto the type comparator directly, so that we can - # ensure "reversed" behavior. - def operate(self, op, *other, **kwargs): - if not operators.is_comparison(op): - raise exc.ArgumentError( - "Only comparison operators may be used with ANY/ALL" - ) - kwargs["reverse"] = True - return self.comparator.operate(operators.mirror(op), *other, **kwargs) - - def reverse_operate(self, op, other, **kwargs): - # comparison operators should never call reverse_operate - assert not operators.is_comparison(op) - raise exc.ArgumentError( - "Only comparison operators may be used with ANY/ALL" - ) - - -class AsBoolean(WrapsColumnExpression[bool], UnaryExpression[bool]): - inherit_cache = True - - def __init__(self, element, operator, negate): - self.element = element - self.type = type_api.BOOLEANTYPE - self.operator = operator - self.negate = negate - self.modifier = None - self.wraps_column_expression = True - self._is_implicitly_boolean = element._is_implicitly_boolean - - @property - def wrapped_column_expression(self): - return self.element - - def self_group(self, against=None): - return self - - def _negate(self): - if isinstance(self.element, (True_, False_)): - return self.element._negate() - else: - return AsBoolean(self.element, self.negate, self.operator) - - -class BinaryExpression(OperatorExpression[_T]): - """Represent an expression that is ``LEFT <operator> RIGHT``. - - A :class:`.BinaryExpression` is generated automatically - whenever two column expressions are used in a Python binary expression: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy.sql import column - >>> column('a') + column('b') - <sqlalchemy.sql.expression.BinaryExpression object at 0x101029dd0> - >>> print(column('a') + column('b')) - {printsql}a + b - - """ - - __visit_name__ = "binary" - - _traverse_internals: _TraverseInternalsType = [ - ("left", InternalTraversal.dp_clauseelement), - ("right", InternalTraversal.dp_clauseelement), - ("operator", InternalTraversal.dp_operator), - ("negate", InternalTraversal.dp_operator), - ("modifiers", InternalTraversal.dp_plain_dict), - ( - "type", - InternalTraversal.dp_type, - ), - ] - - _cache_key_traversal = [ - ("left", InternalTraversal.dp_clauseelement), - ("right", InternalTraversal.dp_clauseelement), - ("operator", InternalTraversal.dp_operator), - ("modifiers", InternalTraversal.dp_plain_dict), - # "type" affects JSON CAST operators, so while redundant in most cases, - # is needed for that one - ( - "type", - InternalTraversal.dp_type, - ), - ] - - _is_implicitly_boolean = True - """Indicates that any database will know this is a boolean expression - even if the database does not have an explicit boolean datatype. - - """ - - modifiers: Optional[Mapping[str, Any]] - - left: ColumnElement[Any] - right: ColumnElement[Any] - - def __init__( - self, - left: ColumnElement[Any], - right: ColumnElement[Any], - operator: OperatorType, - type_: Optional[_TypeEngineArgument[_T]] = None, - negate: Optional[OperatorType] = None, - modifiers: Optional[Mapping[str, Any]] = None, - ): - # allow compatibility with libraries that - # refer to BinaryExpression directly and pass strings - if isinstance(operator, str): - operator = operators.custom_op(operator) - self._orig = (left.__hash__(), right.__hash__()) - self._propagate_attrs = left._propagate_attrs or right._propagate_attrs - self.left = left.self_group(against=operator) - self.right = right.self_group(against=operator) - self.operator = operator - - # if type is None, we get NULLTYPE, which is our _T. But I don't - # know how to get the overloads to express that correctly - self.type = type_api.to_instance(type_) # type: ignore - - self.negate = negate - self._is_implicitly_boolean = operators.is_boolean(operator) - - if modifiers is None: - self.modifiers = {} - else: - self.modifiers = modifiers - - @property - def _flattened_operator_clauses( - self, - ) -> typing_Tuple[ColumnElement[Any], ...]: - return (self.left, self.right) - - def __bool__(self): - """Implement Python-side "bool" for BinaryExpression as a - simple "identity" check for the left and right attributes, - if the operator is "eq" or "ne". Otherwise the expression - continues to not support "bool" like all other column expressions. - - The rationale here is so that ColumnElement objects can be hashable. - What? Well, suppose you do this:: - - c1, c2 = column('x'), column('y') - s1 = set([c1, c2]) - - We do that **a lot**, columns inside of sets is an extremely basic - thing all over the ORM for example. - - So what happens if we do this? :: - - c1 in s1 - - Hashing means it will normally use ``__hash__()`` of the object, - but in case of hash collision, it's going to also do ``c1 == c1`` - and/or ``c1 == c2`` inside. Those operations need to return a - True/False value. But because we override ``==`` and ``!=``, they're - going to get a BinaryExpression. Hence we implement ``__bool__`` here - so that these comparisons behave in this particular context mostly - like regular object comparisons. Thankfully Python is OK with - that! Otherwise we'd have to use special set classes for columns - (which we used to do, decades ago). - - """ - if self.operator in (operators.eq, operators.ne): - # this is using the eq/ne operator given int hash values, - # rather than Operator, so that "bool" can be based on - # identity - return self.operator(*self._orig) # type: ignore - else: - raise TypeError("Boolean value of this clause is not defined") - - if typing.TYPE_CHECKING: - - def __invert__( - self: BinaryExpression[_T], - ) -> BinaryExpression[_T]: ... - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.left._from_objects + self.right._from_objects - - def _negate(self): - if self.negate is not None: - return BinaryExpression( - self.left, - self.right._negate_in_binary(self.negate, self.operator), - self.negate, - negate=self.operator, - type_=self.type, - modifiers=self.modifiers, - ) - else: - return self.self_group()._negate() - - -class Slice(ColumnElement[Any]): - """Represent SQL for a Python array-slice object. - - This is not a specific SQL construct at this level, but - may be interpreted by specific dialects, e.g. PostgreSQL. - - """ - - __visit_name__ = "slice" - - _traverse_internals: _TraverseInternalsType = [ - ("start", InternalTraversal.dp_clauseelement), - ("stop", InternalTraversal.dp_clauseelement), - ("step", InternalTraversal.dp_clauseelement), - ] - - def __init__(self, start, stop, step, _name=None): - self.start = coercions.expect( - roles.ExpressionElementRole, - start, - name=_name, - type_=type_api.INTEGERTYPE, - ) - self.stop = coercions.expect( - roles.ExpressionElementRole, - stop, - name=_name, - type_=type_api.INTEGERTYPE, - ) - self.step = coercions.expect( - roles.ExpressionElementRole, - step, - name=_name, - type_=type_api.INTEGERTYPE, - ) - self.type = type_api.NULLTYPE - - def self_group(self, against=None): - assert against is operator.getitem - return self - - -class IndexExpression(BinaryExpression[Any]): - """Represent the class of expressions that are like an "index" - operation.""" - - inherit_cache = True - - -class GroupedElement(DQLDMLClauseElement): - """Represent any parenthesized expression""" - - __visit_name__ = "grouping" - - element: ClauseElement - - def self_group(self, against=None): - return self - - def _ungroup(self): - return self.element._ungroup() - - -class Grouping(GroupedElement, ColumnElement[_T]): - """Represent a grouping within a column expression""" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("type", InternalTraversal.dp_type), - ] - - _cache_key_traversal = [ - ("element", InternalTraversal.dp_clauseelement), - ] - - element: Union[TextClause, ClauseList, ColumnElement[_T]] - - def __init__( - self, element: Union[TextClause, ClauseList, ColumnElement[_T]] - ): - self.element = element - - # nulltype assignment issue - self.type = getattr(element, "type", type_api.NULLTYPE) # type: ignore - self._propagate_attrs = element._propagate_attrs - - def _with_binary_element_type(self, type_): - return self.__class__(self.element._with_binary_element_type(type_)) - - @util.memoized_property - def _is_implicitly_boolean(self): - return self.element._is_implicitly_boolean - - @util.non_memoized_property - def _tq_label(self) -> Optional[str]: - return ( - getattr(self.element, "_tq_label", None) or self._anon_name_label - ) - - @util.non_memoized_property - def _proxies(self) -> List[ColumnElement[Any]]: - if isinstance(self.element, ColumnElement): - return [self.element] - else: - return [] - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - def __getattr__(self, attr): - return getattr(self.element, attr) - - def __getstate__(self): - return {"element": self.element, "type": self.type} - - def __setstate__(self, state): - self.element = state["element"] - self.type = state["type"] - - -class _OverrideBinds(Grouping[_T]): - """used by cache_key->_apply_params_to_element to allow compilation / - execution of a SQL element that's been cached, using an alternate set of - bound parameter values. - - This is used by the ORM to swap new parameter values into expressions - that are embedded into loader options like with_expression(), - selectinload(). Previously, this task was accomplished using the - .params() method which would perform a deep-copy instead. This deep - copy proved to be too expensive for more complex expressions. - - See #11085 - - """ - - __visit_name__ = "override_binds" - - def __init__( - self, - element: ColumnElement[_T], - bindparams: Sequence[BindParameter[Any]], - replaces_params: Sequence[BindParameter[Any]], - ): - self.element = element - self.translate = { - k.key: v.value for k, v in zip(replaces_params, bindparams) - } - - def _gen_cache_key( - self, anon_map: anon_map, bindparams: List[BindParameter[Any]] - ) -> Optional[typing_Tuple[Any, ...]]: - """generate a cache key for the given element, substituting its bind - values for the translation values present.""" - - existing_bps: List[BindParameter[Any]] = [] - ck = self.element._gen_cache_key(anon_map, existing_bps) - - bindparams.extend( - ( - bp._with_value( - self.translate[bp.key], maintain_key=True, required=False - ) - if bp.key in self.translate - else bp - ) - for bp in existing_bps - ) - - return ck - - -class _OverRange(IntEnum): - RANGE_UNBOUNDED = 0 - RANGE_CURRENT = 1 - - -RANGE_UNBOUNDED = _OverRange.RANGE_UNBOUNDED -RANGE_CURRENT = _OverRange.RANGE_CURRENT - - -class Over(ColumnElement[_T]): - """Represent an OVER clause. - - This is a special operator against a so-called - "window" function, as well as any aggregate function, - which produces results relative to the result set - itself. Most modern SQL backends now support window functions. - - """ - - __visit_name__ = "over" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("order_by", InternalTraversal.dp_clauseelement), - ("partition_by", InternalTraversal.dp_clauseelement), - ("range_", InternalTraversal.dp_plain_obj), - ("rows", InternalTraversal.dp_plain_obj), - ] - - order_by: Optional[ClauseList] = None - partition_by: Optional[ClauseList] = None - - element: ColumnElement[_T] - """The underlying expression object to which this :class:`.Over` - object refers.""" - - range_: Optional[typing_Tuple[int, int]] - - def __init__( - self, - element: ColumnElement[_T], - partition_by: Optional[_ByArgument] = None, - order_by: Optional[_ByArgument] = None, - range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, - rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, - ): - self.element = element - if order_by is not None: - self.order_by = ClauseList( - *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole - ) - if partition_by is not None: - self.partition_by = ClauseList( - *util.to_list(partition_by), - _literal_as_text_role=roles.ByOfRole, - ) - - if range_: - self.range_ = self._interpret_range(range_) - if rows: - raise exc.ArgumentError( - "'range_' and 'rows' are mutually exclusive" - ) - else: - self.rows = None - elif rows: - self.rows = self._interpret_range(rows) - self.range_ = None - else: - self.rows = self.range_ = None - - def __reduce__(self): - return self.__class__, ( - self.element, - self.partition_by, - self.order_by, - self.range_, - self.rows, - ) - - def _interpret_range( - self, range_: typing_Tuple[Optional[int], Optional[int]] - ) -> typing_Tuple[int, int]: - if not isinstance(range_, tuple) or len(range_) != 2: - raise exc.ArgumentError("2-tuple expected for range/rows") - - lower: int - upper: int - - if range_[0] is None: - lower = RANGE_UNBOUNDED - else: - try: - lower = int(range_[0]) - except ValueError as err: - raise exc.ArgumentError( - "Integer or None expected for range value" - ) from err - else: - if lower == 0: - lower = RANGE_CURRENT - - if range_[1] is None: - upper = RANGE_UNBOUNDED - else: - try: - upper = int(range_[1]) - except ValueError as err: - raise exc.ArgumentError( - "Integer or None expected for range value" - ) from err - else: - if upper == 0: - upper = RANGE_CURRENT - - return lower, upper - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - return self.element.type - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list( - itertools.chain( - *[ - c._from_objects - for c in (self.element, self.partition_by, self.order_by) - if c is not None - ] - ) - ) - - -class WithinGroup(ColumnElement[_T]): - """Represent a WITHIN GROUP (ORDER BY) clause. - - This is a special operator against so-called - "ordered set aggregate" and "hypothetical - set aggregate" functions, including ``percentile_cont()``, - ``rank()``, ``dense_rank()``, etc. - - It's supported only by certain database backends, such as PostgreSQL, - Oracle and MS SQL Server. - - The :class:`.WithinGroup` construct extracts its type from the - method :meth:`.FunctionElement.within_group_type`. If this returns - ``None``, the function's ``.type`` is used. - - """ - - __visit_name__ = "withingroup" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("order_by", InternalTraversal.dp_clauseelement), - ] - - order_by: Optional[ClauseList] = None - - def __init__( - self, - element: FunctionElement[_T], - *order_by: _ColumnExpressionArgument[Any], - ): - self.element = element - if order_by is not None: - self.order_by = ClauseList( - *util.to_list(order_by), _literal_as_text_role=roles.ByOfRole - ) - - def __reduce__(self): - return self.__class__, (self.element,) + ( - tuple(self.order_by) if self.order_by is not None else () - ) - - def over(self, partition_by=None, order_by=None, range_=None, rows=None): - """Produce an OVER clause against this :class:`.WithinGroup` - construct. - - This function has the same signature as that of - :meth:`.FunctionElement.over`. - - """ - return Over( - self, - partition_by=partition_by, - order_by=order_by, - range_=range_, - rows=rows, - ) - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - wgt = self.element.within_group_type(self) - if wgt is not None: - return wgt - else: - return self.element.type - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list( - itertools.chain( - *[ - c._from_objects - for c in (self.element, self.order_by) - if c is not None - ] - ) - ) - - -class FunctionFilter(ColumnElement[_T]): - """Represent a function FILTER clause. - - This is a special operator against aggregate and window functions, - which controls which rows are passed to it. - It's supported only by certain database backends. - - Invocation of :class:`.FunctionFilter` is via - :meth:`.FunctionElement.filter`:: - - func.count(1).filter(True) - - .. seealso:: - - :meth:`.FunctionElement.filter` - - """ - - __visit_name__ = "funcfilter" - - _traverse_internals: _TraverseInternalsType = [ - ("func", InternalTraversal.dp_clauseelement), - ("criterion", InternalTraversal.dp_clauseelement), - ] - - criterion: Optional[ColumnElement[bool]] = None - - def __init__( - self, - func: FunctionElement[_T], - *criterion: _ColumnExpressionArgument[bool], - ): - self.func = func - self.filter(*criterion) - - def filter(self, *criterion: _ColumnExpressionArgument[bool]) -> Self: - """Produce an additional FILTER against the function. - - This method adds additional criteria to the initial criteria - set up by :meth:`.FunctionElement.filter`. - - Multiple criteria are joined together at SQL render time - via ``AND``. - - - """ - - for crit in list(criterion): - crit = coercions.expect(roles.WhereHavingRole, crit) - - if self.criterion is not None: - self.criterion = self.criterion & crit - else: - self.criterion = crit - - return self - - def over( - self, - partition_by: Optional[ - Union[ - Iterable[_ColumnExpressionArgument[Any]], - _ColumnExpressionArgument[Any], - ] - ] = None, - order_by: Optional[ - Union[ - Iterable[_ColumnExpressionArgument[Any]], - _ColumnExpressionArgument[Any], - ] - ] = None, - range_: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, - rows: Optional[typing_Tuple[Optional[int], Optional[int]]] = None, - ) -> Over[_T]: - """Produce an OVER clause against this filtered function. - - Used against aggregate or so-called "window" functions, - for database backends that support window functions. - - The expression:: - - func.rank().filter(MyClass.y > 5).over(order_by='x') - - is shorthand for:: - - from sqlalchemy import over, funcfilter - over(funcfilter(func.rank(), MyClass.y > 5), order_by='x') - - See :func:`_expression.over` for a full description. - - """ - return Over( - self, - partition_by=partition_by, - order_by=order_by, - range_=range_, - rows=rows, - ) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> Union[Self, Grouping[_T]]: - if operators.is_precedent(operators.filter_op, against): - return Grouping(self) - else: - return self - - if not TYPE_CHECKING: - - @util.memoized_property - def type(self) -> TypeEngine[_T]: # noqa: A001 - return self.func.type - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return list( - itertools.chain( - *[ - c._from_objects - for c in (self.func, self.criterion) - if c is not None - ] - ) - ) - - -class NamedColumn(KeyedColumnElement[_T]): - is_literal = False - table: Optional[FromClause] = None - name: str - key: str - - def _compare_name_for_result(self, other): - return (hasattr(other, "name") and self.name == other.name) or ( - hasattr(other, "_label") and self._label == other._label - ) - - @util.ro_memoized_property - def description(self) -> str: - return self.name - - @HasMemoized.memoized_attribute - def _tq_key_label(self): - """table qualified label based on column key. - - for table-bound columns this is <tablename>_<column key/proxy key>; - - all other expressions it resolves to key/proxy key. - - """ - proxy_key = self._proxy_key - if proxy_key and proxy_key != self.name: - return self._gen_tq_label(proxy_key) - else: - return self._tq_label - - @HasMemoized.memoized_attribute - def _tq_label(self) -> Optional[str]: - """table qualified label based on column name. - - for table-bound columns this is <tablename>_<columnname>; all other - expressions it resolves to .name. - - """ - return self._gen_tq_label(self.name) - - @HasMemoized.memoized_attribute - def _render_label_in_columns_clause(self): - return True - - @HasMemoized.memoized_attribute - def _non_anon_label(self): - return self.name - - def _gen_tq_label( - self, name: str, dedupe_on_key: bool = True - ) -> Optional[str]: - return name - - def _bind_param( - self, - operator: OperatorType, - obj: Any, - type_: Optional[TypeEngine[_T]] = None, - expanding: bool = False, - ) -> BindParameter[_T]: - return BindParameter( - self.key, - obj, - _compared_to_operator=operator, - _compared_to_type=self.type, - type_=type_, - unique=True, - expanding=expanding, - ) - - def _make_proxy( - self, - selectable: FromClause, - *, - name: Optional[str] = None, - key: Optional[str] = None, - name_is_truncatable: bool = False, - compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None, - disallow_is_literal: bool = False, - **kw: Any, - ) -> typing_Tuple[str, ColumnClause[_T]]: - c = ColumnClause( - ( - coercions.expect(roles.TruncatedLabelRole, name or self.name) - if name_is_truncatable - else (name or self.name) - ), - type_=self.type, - _selectable=selectable, - is_literal=False, - ) - - c._propagate_attrs = selectable._propagate_attrs - if name is None: - c.key = self.key - if compound_select_cols: - c._proxies = list(compound_select_cols) - else: - c._proxies = [self] - - if selectable._is_clone_of is not None: - c._is_clone_of = selectable._is_clone_of.columns.get(c.key) - return c.key, c - - -class Label(roles.LabeledColumnExprRole[_T], NamedColumn[_T]): - """Represents a column label (AS). - - Represent a label, as typically applied to any column-level - element using the ``AS`` sql keyword. - - """ - - __visit_name__ = "label" - - _traverse_internals: _TraverseInternalsType = [ - ("name", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("_element", InternalTraversal.dp_clauseelement), - ] - - _cache_key_traversal = [ - ("name", InternalTraversal.dp_anon_name), - ("_element", InternalTraversal.dp_clauseelement), - ] - - _element: ColumnElement[_T] - name: str - - def __init__( - self, - name: Optional[str], - element: _ColumnExpressionArgument[_T], - type_: Optional[_TypeEngineArgument[_T]] = None, - ): - orig_element = element - element = coercions.expect( - roles.ExpressionElementRole, - element, - apply_propagate_attrs=self, - ) - while isinstance(element, Label): - # TODO: this is only covered in test_text.py, but nothing - # fails if it's removed. determine rationale - element = element.element - - if name: - self.name = name - else: - self.name = _anonymous_label.safe_construct( - id(self), getattr(element, "name", "anon") - ) - if isinstance(orig_element, Label): - # TODO: no coverage for this block, again would be in - # test_text.py where the resolve_label concept is important - self._resolve_label = orig_element._label - - self.key = self._tq_label = self._tq_key_label = self.name - self._element = element - - self.type = ( - type_api.to_instance(type_) - if type_ is not None - else self._element.type - ) - - self._proxies = [element] - - def __reduce__(self): - return self.__class__, (self.name, self._element, self.type) - - @HasMemoized.memoized_attribute - def _render_label_in_columns_clause(self): - return True - - def _bind_param(self, operator, obj, type_=None, expanding=False): - return BindParameter( - None, - obj, - _compared_to_operator=operator, - type_=type_, - _compared_to_type=self.type, - unique=True, - expanding=expanding, - ) - - @util.memoized_property - def _is_implicitly_boolean(self): - return self.element._is_implicitly_boolean - - @HasMemoized.memoized_attribute - def _allow_label_resolve(self): - return self.element._allow_label_resolve - - @property - def _order_by_label_element(self): - return self - - @HasMemoized.memoized_attribute - def element(self) -> ColumnElement[_T]: - return self._element.self_group(against=operators.as_) - - def self_group(self, against=None): - return self._apply_to_inner(self._element.self_group, against=against) - - def _negate(self): - return self._apply_to_inner(self._element._negate) - - def _apply_to_inner(self, fn, *arg, **kw): - sub_element = fn(*arg, **kw) - if sub_element is not self._element: - return Label(self.name, sub_element, type_=self.type) - else: - return self - - @property - def primary_key(self): - return self.element.primary_key - - @property - def foreign_keys(self): - return self.element.foreign_keys - - def _copy_internals( - self, - *, - clone: _CloneCallableType = _clone, - anonymize_labels: bool = False, - **kw: Any, - ) -> None: - self._reset_memoizations() - self._element = clone(self._element, **kw) - if anonymize_labels: - self.name = _anonymous_label.safe_construct( - id(self), getattr(self.element, "name", "anon") - ) - self.key = self._tq_label = self._tq_key_label = self.name - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - def _make_proxy( - self, - selectable: FromClause, - *, - name: Optional[str] = None, - compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None, - **kw: Any, - ) -> typing_Tuple[str, ColumnClause[_T]]: - name = self.name if not name else name - - key, e = self.element._make_proxy( - selectable, - name=name, - disallow_is_literal=True, - name_is_truncatable=isinstance(name, _truncated_label), - compound_select_cols=compound_select_cols, - ) - - # there was a note here to remove this assertion, which was here - # to determine if we later could support a use case where - # the key and name of a label are separate. But I don't know what - # that case was. For now, this is an unexpected case that occurs - # when a label name conflicts with other columns and select() - # is attempting to disambiguate an explicit label, which is not what - # the user would want. See issue #6090. - if key != self.name and not isinstance(self.name, _anonymous_label): - raise exc.InvalidRequestError( - "Label name %s is being renamed to an anonymous label due " - "to disambiguation " - "which is not supported right now. Please use unique names " - "for explicit labels." % (self.name) - ) - - e._propagate_attrs = selectable._propagate_attrs - e._proxies.append(self) - if self.type is not None: - e.type = self.type - - return self.key, e - - -class ColumnClause( - roles.DDLReferredColumnRole, - roles.LabeledColumnExprRole[_T], - roles.StrAsPlainColumnRole, - Immutable, - NamedColumn[_T], -): - """Represents a column expression from any textual string. - - The :class:`.ColumnClause`, a lightweight analogue to the - :class:`_schema.Column` class, is typically invoked using the - :func:`_expression.column` function, as in:: - - from sqlalchemy import column - - id, name = column("id"), column("name") - stmt = select(id, name).select_from("user") - - The above statement would produce SQL like:: - - SELECT id, name FROM user - - :class:`.ColumnClause` is the immediate superclass of the schema-specific - :class:`_schema.Column` object. While the :class:`_schema.Column` - class has all the - same capabilities as :class:`.ColumnClause`, the :class:`.ColumnClause` - class is usable by itself in those cases where behavioral requirements - are limited to simple SQL expression generation. The object has none of - the associations with schema-level metadata or with execution-time - behavior that :class:`_schema.Column` does, - so in that sense is a "lightweight" - version of :class:`_schema.Column`. - - Full details on :class:`.ColumnClause` usage is at - :func:`_expression.column`. - - .. seealso:: - - :func:`_expression.column` - - :class:`_schema.Column` - - """ - - table: Optional[FromClause] - is_literal: bool - - __visit_name__ = "column" - - _traverse_internals: _TraverseInternalsType = [ - ("name", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("table", InternalTraversal.dp_clauseelement), - ("is_literal", InternalTraversal.dp_boolean), - ] - - onupdate: Optional[DefaultGenerator] = None - default: Optional[DefaultGenerator] = None - server_default: Optional[FetchedValue] = None - server_onupdate: Optional[FetchedValue] = None - - _is_multiparam_column = False - - @property - def _is_star(self): - return self.is_literal and self.name == "*" - - def __init__( - self, - text: str, - type_: Optional[_TypeEngineArgument[_T]] = None, - is_literal: bool = False, - _selectable: Optional[FromClause] = None, - ): - self.key = self.name = text - self.table = _selectable - - # if type is None, we get NULLTYPE, which is our _T. But I don't - # know how to get the overloads to express that correctly - self.type = type_api.to_instance(type_) # type: ignore - - self.is_literal = is_literal - - def get_children(self, *, column_tables=False, **kw): - # override base get_children() to not return the Table - # or selectable that is parent to this column. Traversals - # expect the columns of tables and subqueries to be leaf nodes. - return [] - - @property - def entity_namespace(self): - if self.table is not None: - return self.table.entity_namespace - else: - return super().entity_namespace - - def _clone(self, detect_subquery_cols=False, **kw): - if ( - detect_subquery_cols - and self.table is not None - and self.table._is_subquery - ): - clone = kw.pop("clone") - table = clone(self.table, **kw) - new = table.c.corresponding_column(self) - return new - - return super()._clone(**kw) - - @HasMemoized_ro_memoized_attribute - def _from_objects(self) -> List[FromClause]: - t = self.table - if t is not None: - return [t] - else: - return [] - - @HasMemoized.memoized_attribute - def _render_label_in_columns_clause(self): - return self.table is not None - - @property - def _ddl_label(self): - return self._gen_tq_label(self.name, dedupe_on_key=False) - - def _compare_name_for_result(self, other): - if ( - self.is_literal - or self.table is None - or self.table._is_textual - or not hasattr(other, "proxy_set") - or ( - isinstance(other, ColumnClause) - and ( - other.is_literal - or other.table is None - or other.table._is_textual - ) - ) - ): - return (hasattr(other, "name") and self.name == other.name) or ( - hasattr(other, "_tq_label") - and self._tq_label == other._tq_label - ) - else: - return other.proxy_set.intersection(self.proxy_set) - - def _gen_tq_label( - self, name: str, dedupe_on_key: bool = True - ) -> Optional[str]: - """generate table-qualified label - - for a table-bound column this is <tablename>_<columnname>. - - used primarily for LABEL_STYLE_TABLENAME_PLUS_COL - as well as the .columns collection on a Join object. - - """ - label: str - t = self.table - if self.is_literal: - return None - elif t is not None and is_named_from_clause(t): - if has_schema_attr(t) and t.schema: - label = t.schema.replace(".", "_") + "_" + t.name + "_" + name - else: - assert not TYPE_CHECKING or isinstance(t, NamedFromClause) - label = t.name + "_" + name - - # propagate name quoting rules for labels. - if is_quoted_name(name) and name.quote is not None: - if is_quoted_name(label): - label.quote = name.quote - else: - label = quoted_name(label, name.quote) - elif is_quoted_name(t.name) and t.name.quote is not None: - # can't get this situation to occur, so let's - # assert false on it for now - assert not isinstance(label, quoted_name) - label = quoted_name(label, t.name.quote) - - if dedupe_on_key: - # ensure the label name doesn't conflict with that of an - # existing column. note that this implies that any Column - # must **not** set up its _label before its parent table has - # all of its other Column objects set up. There are several - # tables in the test suite which will fail otherwise; example: - # table "owner" has columns "name" and "owner_name". Therefore - # column owner.name cannot use the label "owner_name", it has - # to be "owner_name_1". - if label in t.c: - _label = label - counter = 1 - while _label in t.c: - _label = label + "_" + str(counter) - counter += 1 - label = _label - - return coercions.expect(roles.TruncatedLabelRole, label) - - else: - return name - - def _make_proxy( - self, - selectable: FromClause, - *, - name: Optional[str] = None, - key: Optional[str] = None, - name_is_truncatable: bool = False, - compound_select_cols: Optional[Sequence[ColumnElement[Any]]] = None, - disallow_is_literal: bool = False, - **kw: Any, - ) -> typing_Tuple[str, ColumnClause[_T]]: - # the "is_literal" flag normally should never be propagated; a proxied - # column is always a SQL identifier and never the actual expression - # being evaluated. however, there is a case where the "is_literal" flag - # might be used to allow the given identifier to have a fixed quoting - # pattern already, so maintain the flag for the proxy unless a - # :class:`.Label` object is creating the proxy. See [ticket:4730]. - is_literal = ( - not disallow_is_literal - and self.is_literal - and ( - # note this does not accommodate for quoted_name differences - # right now - name is None - or name == self.name - ) - ) - c = self._constructor( - ( - coercions.expect(roles.TruncatedLabelRole, name or self.name) - if name_is_truncatable - else (name or self.name) - ), - type_=self.type, - _selectable=selectable, - is_literal=is_literal, - ) - c._propagate_attrs = selectable._propagate_attrs - if name is None: - c.key = self.key - if compound_select_cols: - c._proxies = list(compound_select_cols) - else: - c._proxies = [self] - - if selectable._is_clone_of is not None: - c._is_clone_of = selectable._is_clone_of.columns.get(c.key) - return c.key, c - - -class TableValuedColumn(NamedColumn[_T]): - __visit_name__ = "table_valued_column" - - _traverse_internals: _TraverseInternalsType = [ - ("name", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("scalar_alias", InternalTraversal.dp_clauseelement), - ] - - def __init__(self, scalar_alias: NamedFromClause, type_: TypeEngine[_T]): - self.scalar_alias = scalar_alias - self.key = self.name = scalar_alias.name - self.type = type_ - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - self.scalar_alias = clone(self.scalar_alias, **kw) - self.key = self.name = self.scalar_alias.name - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [self.scalar_alias] - - -class CollationClause(ColumnElement[str]): - __visit_name__ = "collation" - - _traverse_internals: _TraverseInternalsType = [ - ("collation", InternalTraversal.dp_string) - ] - - @classmethod - def _create_collation_expression( - cls, expression: _ColumnExpressionArgument[str], collation: str - ) -> BinaryExpression[str]: - expr = coercions.expect(roles.ExpressionElementRole[str], expression) - return BinaryExpression( - expr, - CollationClause(collation), - operators.collate, - type_=expr.type, - ) - - def __init__(self, collation): - self.collation = collation - - -class _IdentifiedClause(Executable, ClauseElement): - __visit_name__ = "identified" - - def __init__(self, ident): - self.ident = ident - - -class SavepointClause(_IdentifiedClause): - __visit_name__ = "savepoint" - inherit_cache = False - - -class RollbackToSavepointClause(_IdentifiedClause): - __visit_name__ = "rollback_to_savepoint" - inherit_cache = False - - -class ReleaseSavepointClause(_IdentifiedClause): - __visit_name__ = "release_savepoint" - inherit_cache = False - - -class quoted_name(util.MemoizedSlots, str): - """Represent a SQL identifier combined with quoting preferences. - - :class:`.quoted_name` is a Python unicode/str subclass which - represents a particular identifier name along with a - ``quote`` flag. This ``quote`` flag, when set to - ``True`` or ``False``, overrides automatic quoting behavior - for this identifier in order to either unconditionally quote - or to not quote the name. If left at its default of ``None``, - quoting behavior is applied to the identifier on a per-backend basis - based on an examination of the token itself. - - A :class:`.quoted_name` object with ``quote=True`` is also - prevented from being modified in the case of a so-called - "name normalize" option. Certain database backends, such as - Oracle, Firebird, and DB2 "normalize" case-insensitive names - as uppercase. The SQLAlchemy dialects for these backends - convert from SQLAlchemy's lower-case-means-insensitive convention - to the upper-case-means-insensitive conventions of those backends. - The ``quote=True`` flag here will prevent this conversion from occurring - to support an identifier that's quoted as all lower case against - such a backend. - - The :class:`.quoted_name` object is normally created automatically - when specifying the name for key schema constructs such as - :class:`_schema.Table`, :class:`_schema.Column`, and others. - The class can also be - passed explicitly as the name to any function that receives a name which - can be quoted. Such as to use the :meth:`_engine.Engine.has_table` - method with - an unconditionally quoted name:: - - from sqlalchemy import create_engine - from sqlalchemy import inspect - from sqlalchemy.sql import quoted_name - - engine = create_engine("oracle+cx_oracle://some_dsn") - print(inspect(engine).has_table(quoted_name("some_table", True))) - - The above logic will run the "has table" logic against the Oracle backend, - passing the name exactly as ``"some_table"`` without converting to - upper case. - - .. versionchanged:: 1.2 The :class:`.quoted_name` construct is now - importable from ``sqlalchemy.sql``, in addition to the previous - location of ``sqlalchemy.sql.elements``. - - """ - - __slots__ = "quote", "lower", "upper" - - quote: Optional[bool] - - @overload - @classmethod - def construct(cls, value: str, quote: Optional[bool]) -> quoted_name: ... - - @overload - @classmethod - def construct(cls, value: None, quote: Optional[bool]) -> None: ... - - @classmethod - def construct( - cls, value: Optional[str], quote: Optional[bool] - ) -> Optional[quoted_name]: - if value is None: - return None - else: - return quoted_name(value, quote) - - def __new__(cls, value: str, quote: Optional[bool]) -> quoted_name: - assert ( - value is not None - ), "use quoted_name.construct() for None passthrough" - if isinstance(value, cls) and (quote is None or value.quote == quote): - return value - self = super().__new__(cls, value) - - self.quote = quote - return self - - def __reduce__(self): - return quoted_name, (str(self), self.quote) - - def _memoized_method_lower(self): - if self.quote: - return self - else: - return str(self).lower() - - def _memoized_method_upper(self): - if self.quote: - return self - else: - return str(self).upper() - - -def _find_columns(clause: ClauseElement) -> Set[ColumnClause[Any]]: - """locate Column objects within the given expression.""" - - cols: Set[ColumnClause[Any]] = set() - traverse(clause, {}, {"column": cols.add}) - return cols - - -def _type_from_args(args: Sequence[ColumnElement[_T]]) -> TypeEngine[_T]: - for a in args: - if not a.type._isnull: - return a.type - else: - return type_api.NULLTYPE # type: ignore - - -def _corresponding_column_or_error(fromclause, column, require_embedded=False): - c = fromclause.corresponding_column( - column, require_embedded=require_embedded - ) - if c is None: - raise exc.InvalidRequestError( - "Given column '%s', attached to table '%s', " - "failed to locate a corresponding column from table '%s'" - % (column, getattr(column, "table", None), fromclause.description) - ) - return c - - -class _memoized_property_but_not_nulltype( - util.memoized_property["TypeEngine[_T]"] -): - """memoized property, but dont memoize NullType""" - - def __get__(self, obj, cls): - if obj is None: - return self - result = self.fget(obj) - if not result._isnull: - obj.__dict__[self.__name__] = result - return result - - -class AnnotatedColumnElement(Annotated): - _Annotated__element: ColumnElement[Any] - - def __init__(self, element, values): - Annotated.__init__(self, element, values) - for attr in ( - "comparator", - "_proxy_key", - "_tq_key_label", - "_tq_label", - "_non_anon_label", - "type", - ): - self.__dict__.pop(attr, None) - for attr in ("name", "key", "table"): - if self.__dict__.get(attr, False) is None: - self.__dict__.pop(attr) - - def _with_annotations(self, values): - clone = super()._with_annotations(values) - clone.__dict__.pop("comparator", None) - return clone - - @util.memoized_property - def name(self): - """pull 'name' from parent, if not present""" - return self._Annotated__element.name - - @_memoized_property_but_not_nulltype - def type(self): - """pull 'type' from parent and don't cache if null. - - type is routinely changed on existing columns within the - mapped_column() initialization process, and "type" is also consulted - during the creation of SQL expressions. Therefore it can change after - it was already retrieved. At the same time we don't want annotated - objects having overhead when expressions are produced, so continue - to memoize, but only when we have a non-null type. - - """ - return self._Annotated__element.type - - @util.memoized_property - def table(self): - """pull 'table' from parent, if not present""" - return self._Annotated__element.table - - @util.memoized_property - def key(self): - """pull 'key' from parent, if not present""" - return self._Annotated__element.key - - @util.memoized_property - def info(self) -> _InfoType: - if TYPE_CHECKING: - assert isinstance(self._Annotated__element, Column) - return self._Annotated__element.info - - @util.memoized_property - def _anon_name_label(self) -> str: - return self._Annotated__element._anon_name_label - - -class _truncated_label(quoted_name): - """A unicode subclass used to identify symbolic " - "names that may require truncation.""" - - __slots__ = () - - def __new__(cls, value: str, quote: Optional[bool] = None) -> Any: - quote = getattr(value, "quote", quote) - # return super(_truncated_label, cls).__new__(cls, value, quote, True) - return super().__new__(cls, value, quote) - - def __reduce__(self) -> Any: - return self.__class__, (str(self), self.quote) - - def apply_map(self, map_: Mapping[str, Any]) -> str: - return self - - -class conv(_truncated_label): - """Mark a string indicating that a name has already been converted - by a naming convention. - - This is a string subclass that indicates a name that should not be - subject to any further naming conventions. - - E.g. when we create a :class:`.Constraint` using a naming convention - as follows:: - - m = MetaData(naming_convention={ - "ck": "ck_%(table_name)s_%(constraint_name)s" - }) - t = Table('t', m, Column('x', Integer), - CheckConstraint('x > 5', name='x5')) - - The name of the above constraint will be rendered as ``"ck_t_x5"``. - That is, the existing name ``x5`` is used in the naming convention as the - ``constraint_name`` token. - - In some situations, such as in migration scripts, we may be rendering - the above :class:`.CheckConstraint` with a name that's already been - converted. In order to make sure the name isn't double-modified, the - new name is applied using the :func:`_schema.conv` marker. We can - use this explicitly as follows:: - - - m = MetaData(naming_convention={ - "ck": "ck_%(table_name)s_%(constraint_name)s" - }) - t = Table('t', m, Column('x', Integer), - CheckConstraint('x > 5', name=conv('ck_t_x5'))) - - Where above, the :func:`_schema.conv` marker indicates that the constraint - name here is final, and the name will render as ``"ck_t_x5"`` and not - ``"ck_t_ck_t_x5"`` - - .. seealso:: - - :ref:`constraint_naming_conventions` - - """ - - __slots__ = () - - -# for backwards compatibility in case -# someone is re-implementing the -# _truncated_identifier() sequence in a custom -# compiler -_generated_label = _truncated_label - - -class _anonymous_label(_truncated_label): - """A unicode subclass used to identify anonymously - generated names.""" - - __slots__ = () - - @classmethod - def safe_construct( - cls, - seed: int, - body: str, - enclosing_label: Optional[str] = None, - sanitize_key: bool = False, - ) -> _anonymous_label: - # need to escape chars that interfere with format - # strings in any case, issue #8724 - body = re.sub(r"[%\(\) \$]+", "_", body) - - if sanitize_key: - # sanitize_key is then an extra step used by BindParameter - body = body.strip("_") - - label = "%%(%d %s)s" % (seed, body.replace("%", "%%")) - if enclosing_label: - label = "%s%s" % (enclosing_label, label) - - return _anonymous_label(label) - - def __add__(self, other): - if "%" in other and not isinstance(other, _anonymous_label): - other = str(other).replace("%", "%%") - else: - other = str(other) - - return _anonymous_label( - quoted_name( - str.__add__(self, other), - self.quote, - ) - ) - - def __radd__(self, other): - if "%" in other and not isinstance(other, _anonymous_label): - other = str(other).replace("%", "%%") - else: - other = str(other) - - return _anonymous_label( - quoted_name( - str.__add__(other, self), - self.quote, - ) - ) - - def apply_map(self, map_): - if self.quote is not None: - # preserve quoting only if necessary - return quoted_name(self % map_, self.quote) - else: - # else skip the constructor call - return self % map_ diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/events.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/events.py deleted file mode 100644 index 1a6a9a6..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/events.py +++ /dev/null @@ -1,455 +0,0 @@ -# sql/events.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -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` - - """ - - _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 - - """ diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/expression.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/expression.py deleted file mode 100644 index ba42445..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/expression.py +++ /dev/null @@ -1,162 +0,0 @@ -# sql/expression.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""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.""" diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/functions.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/functions.py deleted file mode 100644 index afb2b1d..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/functions.py +++ /dev/null @@ -1,2052 +0,0 @@ -# sql/functions.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - - -"""SQL function API, factories, and built-in functions. - -""" - -from __future__ import annotations - -import datetime -import decimal -from typing import Any -from typing import cast -from typing import Dict -from typing import List -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 typing import Union - -from . import annotation -from . import coercions -from . import operators -from . import roles -from . import schema -from . import sqltypes -from . import type_api -from . import util as sqlutil -from ._typing import is_table_value_type -from .base import _entity_namespace -from .base import ColumnCollection -from .base import Executable -from .base import Generative -from .base import HasMemoized -from .elements import _type_from_args -from .elements import BinaryExpression -from .elements import BindParameter -from .elements import Cast -from .elements import ClauseList -from .elements import ColumnElement -from .elements import Extract -from .elements import FunctionFilter -from .elements import Grouping -from .elements import literal_column -from .elements import NamedColumn -from .elements import Over -from .elements import WithinGroup -from .selectable import FromClause -from .selectable import Select -from .selectable import TableValuedAlias -from .sqltypes import TableValueType -from .type_api import TypeEngine -from .visitors import InternalTraversal -from .. import util - - -if TYPE_CHECKING: - from ._typing import _ByArgument - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnExpressionOrLiteralArgument - from ._typing import _ColumnExpressionOrStrLabelArgument - from ._typing import _TypeEngineArgument - from .base import _EntityNamespace - from .elements import ClauseElement - from .elements import KeyedColumnElement - from .elements import TableValuedColumn - from .operators import OperatorType - from ..engine.base import Connection - from ..engine.cursor import CursorResult - from ..engine.interfaces import _CoreMultiExecuteParams - from ..engine.interfaces import CoreExecuteOptionsParameter - from ..util.typing import Self - -_T = TypeVar("_T", bound=Any) -_S = TypeVar("_S", bound=Any) - -_registry: util.defaultdict[str, Dict[str, Type[Function[Any]]]] = ( - util.defaultdict(dict) -) - - -def register_function( - identifier: str, fn: Type[Function[Any]], package: str = "_default" -) -> None: - """Associate a callable with a particular func. name. - - This is normally called by GenericFunction, but is also - available by itself so that a non-Function construct - can be associated with the :data:`.func` accessor (i.e. - CAST, EXTRACT). - - """ - reg = _registry[package] - - identifier = str(identifier).lower() - - # Check if a function with the same identifier is registered. - if identifier in reg: - util.warn( - "The GenericFunction '{}' is already registered and " - "is going to be overridden.".format(identifier) - ) - reg[identifier] = fn - - -class FunctionElement(Executable, ColumnElement[_T], FromClause, Generative): - """Base for SQL function-oriented constructs. - - This is a `generic type <https://peps.python.org/pep-0484/#generics>`_, - meaning that type checkers and IDEs can be instructed on the types to - expect in a :class:`_engine.Result` for this function. See - :class:`.GenericFunction` for an example of how this is done. - - .. seealso:: - - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` - - :class:`.Function` - named SQL function. - - :data:`.func` - namespace which produces registered or ad-hoc - :class:`.Function` instances. - - :class:`.GenericFunction` - allows creation of registered function - types. - - """ - - _traverse_internals = [ - ("clause_expr", InternalTraversal.dp_clauseelement), - ("_with_ordinality", InternalTraversal.dp_boolean), - ("_table_value_type", InternalTraversal.dp_has_cache_key), - ] - - packagenames: Tuple[str, ...] = () - - _has_args = False - _with_ordinality = False - _table_value_type: Optional[TableValueType] = None - - # some attributes that are defined between both ColumnElement and - # FromClause are set to Any here to avoid typing errors - primary_key: Any - _is_clone_of: Any - - clause_expr: Grouping[Any] - - def __init__(self, *clauses: _ColumnExpressionOrLiteralArgument[Any]): - r"""Construct a :class:`.FunctionElement`. - - :param \*clauses: list of column expressions that form the arguments - of the SQL function call. - - :param \**kwargs: additional kwargs are typically consumed by - subclasses. - - .. seealso:: - - :data:`.func` - - :class:`.Function` - - """ - args: Sequence[_ColumnExpressionArgument[Any]] = [ - coercions.expect( - roles.ExpressionElementRole, - c, - name=getattr(self, "name", None), - apply_propagate_attrs=self, - ) - for c in clauses - ] - self._has_args = self._has_args or bool(args) - self.clause_expr = Grouping( - ClauseList(operator=operators.comma_op, group_contents=True, *args) - ) - - _non_anon_label = None - - @property - def _proxy_key(self) -> Any: - return super()._proxy_key or getattr(self, "name", None) - - def _execute_on_connection( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> CursorResult[Any]: - return connection._execute_function( - self, distilled_params, execution_options - ) - - def scalar_table_valued( - self, name: str, type_: Optional[_TypeEngineArgument[_T]] = None - ) -> ScalarFunctionColumn[_T]: - """Return a column expression that's against this - :class:`_functions.FunctionElement` as a scalar - table-valued expression. - - The returned expression is similar to that returned by a single column - accessed off of a :meth:`_functions.FunctionElement.table_valued` - construct, except no FROM clause is generated; the function is rendered - in the similar way as a scalar subquery. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import func, select - >>> fn = func.jsonb_each("{'k', 'v'}").scalar_table_valued("key") - >>> print(select(fn)) - {printsql}SELECT (jsonb_each(:jsonb_each_1)).key - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :meth:`_functions.FunctionElement.table_valued` - - :meth:`_functions.FunctionElement.alias` - - :meth:`_functions.FunctionElement.column_valued` - - """ # noqa: E501 - - return ScalarFunctionColumn(self, name, type_) - - def table_valued( - self, *expr: _ColumnExpressionOrStrLabelArgument[Any], **kw: Any - ) -> TableValuedAlias: - r"""Return a :class:`_sql.TableValuedAlias` representation of this - :class:`_functions.FunctionElement` with table-valued expressions added. - - e.g.: - - .. sourcecode:: pycon+sql - - >>> fn = ( - ... func.generate_series(1, 5). - ... table_valued("value", "start", "stop", "step") - ... ) - - >>> print(select(fn)) - {printsql}SELECT anon_1.value, anon_1.start, anon_1.stop, anon_1.step - FROM generate_series(:generate_series_1, :generate_series_2) AS anon_1{stop} - - >>> print(select(fn.c.value, fn.c.stop).where(fn.c.value > 2)) - {printsql}SELECT anon_1.value, anon_1.stop - FROM generate_series(:generate_series_1, :generate_series_2) AS anon_1 - WHERE anon_1.value > :value_1{stop} - - A WITH ORDINALITY expression may be generated by passing the keyword - argument "with_ordinality": - - .. sourcecode:: pycon+sql - - >>> fn = func.generate_series(4, 1, -1).table_valued("gen", with_ordinality="ordinality") - >>> print(select(fn)) - {printsql}SELECT anon_1.gen, anon_1.ordinality - FROM generate_series(:generate_series_1, :generate_series_2, :generate_series_3) WITH ORDINALITY AS anon_1 - - :param \*expr: A series of string column names that will be added to the - ``.c`` collection of the resulting :class:`_sql.TableValuedAlias` - construct as columns. :func:`_sql.column` objects with or without - datatypes may also be used. - - :param name: optional name to assign to the alias name that's generated. - If omitted, a unique anonymizing name is used. - - :param with_ordinality: string name that when present results in the - ``WITH ORDINALITY`` clause being added to the alias, and the given - string name will be added as a column to the .c collection - of the resulting :class:`_sql.TableValuedAlias`. - - :param joins_implicitly: when True, the table valued function may be - used in the FROM clause without any explicit JOIN to other tables - in the SQL query, and no "cartesian product" warning will be generated. - May be useful for SQL functions such as ``func.json_each()``. - - .. versionadded:: 1.4.33 - - .. versionadded:: 1.4.0b2 - - - .. seealso:: - - :ref:`tutorial_functions_table_valued` - in the :ref:`unified_tutorial` - - :ref:`postgresql_table_valued` - in the :ref:`postgresql_toplevel` documentation - - :meth:`_functions.FunctionElement.scalar_table_valued` - variant of - :meth:`_functions.FunctionElement.table_valued` which delivers the - complete table valued expression as a scalar column expression - - :meth:`_functions.FunctionElement.column_valued` - - :meth:`_sql.TableValuedAlias.render_derived` - renders the alias - using a derived column clause, e.g. ``AS name(col1, col2, ...)`` - - """ # noqa: 501 - - new_func = self._generate() - - with_ordinality = kw.pop("with_ordinality", None) - joins_implicitly = kw.pop("joins_implicitly", None) - name = kw.pop("name", None) - - if with_ordinality: - expr += (with_ordinality,) - new_func._with_ordinality = True - - new_func.type = new_func._table_value_type = TableValueType(*expr) - - return new_func.alias(name=name, joins_implicitly=joins_implicitly) - - def column_valued( - self, name: Optional[str] = None, joins_implicitly: bool = False - ) -> TableValuedColumn[_T]: - """Return this :class:`_functions.FunctionElement` as a column expression that - selects from itself as a FROM clause. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import select, func - >>> gs = func.generate_series(1, 5, -1).column_valued() - >>> print(select(gs)) - {printsql}SELECT anon_1 - FROM generate_series(:generate_series_1, :generate_series_2, :generate_series_3) AS anon_1 - - This is shorthand for:: - - gs = func.generate_series(1, 5, -1).alias().column - - :param name: optional name to assign to the alias name that's generated. - If omitted, a unique anonymizing name is used. - - :param joins_implicitly: when True, the "table" portion of the column - valued function may be a member of the FROM clause without any - explicit JOIN to other tables in the SQL query, and no "cartesian - product" warning will be generated. May be useful for SQL functions - such as ``func.json_array_elements()``. - - .. versionadded:: 1.4.46 - - .. seealso:: - - :ref:`tutorial_functions_column_valued` - in the :ref:`unified_tutorial` - - :ref:`postgresql_column_valued` - in the :ref:`postgresql_toplevel` documentation - - :meth:`_functions.FunctionElement.table_valued` - - """ # noqa: 501 - - return self.alias(name=name, joins_implicitly=joins_implicitly).column - - @util.ro_non_memoized_property - def columns(self) -> ColumnCollection[str, KeyedColumnElement[Any]]: # type: ignore[override] # noqa: E501 - r"""The set of columns exported by this :class:`.FunctionElement`. - - This is a placeholder collection that allows the function to be - placed in the FROM clause of a statement: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import column, select, func - >>> stmt = select(column('x'), column('y')).select_from(func.myfunction()) - >>> print(stmt) - {printsql}SELECT x, y FROM myfunction() - - The above form is a legacy feature that is now superseded by the - fully capable :meth:`_functions.FunctionElement.table_valued` - method; see that method for details. - - .. seealso:: - - :meth:`_functions.FunctionElement.table_valued` - generates table-valued - SQL function expressions. - - """ # noqa: E501 - return self.c - - @util.ro_memoized_property - def c(self) -> ColumnCollection[str, KeyedColumnElement[Any]]: # type: ignore[override] # noqa: E501 - """synonym for :attr:`.FunctionElement.columns`.""" - - return ColumnCollection( - columns=[(col.key, col) for col in self._all_selected_columns] - ) - - @property - def _all_selected_columns(self) -> Sequence[KeyedColumnElement[Any]]: - if is_table_value_type(self.type): - # TODO: this might not be fully accurate - cols = cast( - "Sequence[KeyedColumnElement[Any]]", self.type._elements - ) - else: - cols = [self.label(None)] - - return cols - - @property - def exported_columns( # type: ignore[override] - self, - ) -> ColumnCollection[str, KeyedColumnElement[Any]]: - return self.columns - - @HasMemoized.memoized_attribute - def clauses(self) -> ClauseList: - """Return the underlying :class:`.ClauseList` which contains - the arguments for this :class:`.FunctionElement`. - - """ - return cast(ClauseList, self.clause_expr.element) - - def over( - self, - *, - partition_by: Optional[_ByArgument] = None, - order_by: Optional[_ByArgument] = None, - rows: Optional[Tuple[Optional[int], Optional[int]]] = None, - range_: Optional[Tuple[Optional[int], Optional[int]]] = None, - ) -> Over[_T]: - """Produce an OVER clause against this function. - - Used against aggregate or so-called "window" functions, - for database backends that support window functions. - - The expression:: - - func.row_number().over(order_by='x') - - is shorthand for:: - - from sqlalchemy import over - over(func.row_number(), order_by='x') - - See :func:`_expression.over` for a full description. - - .. seealso:: - - :func:`_expression.over` - - :ref:`tutorial_window_functions` - in the :ref:`unified_tutorial` - - """ - return Over( - self, - partition_by=partition_by, - order_by=order_by, - rows=rows, - range_=range_, - ) - - def within_group( - self, *order_by: _ColumnExpressionArgument[Any] - ) -> WithinGroup[_T]: - """Produce a WITHIN GROUP (ORDER BY expr) clause against this function. - - Used against so-called "ordered set aggregate" and "hypothetical - set aggregate" functions, including :class:`.percentile_cont`, - :class:`.rank`, :class:`.dense_rank`, etc. - - See :func:`_expression.within_group` for a full description. - - .. seealso:: - - :ref:`tutorial_functions_within_group` - - in the :ref:`unified_tutorial` - - - """ - return WithinGroup(self, *order_by) - - @overload - def filter(self) -> Self: ... - - @overload - def filter( - self, - __criterion0: _ColumnExpressionArgument[bool], - *criterion: _ColumnExpressionArgument[bool], - ) -> FunctionFilter[_T]: ... - - def filter( - self, *criterion: _ColumnExpressionArgument[bool] - ) -> Union[Self, FunctionFilter[_T]]: - """Produce a FILTER clause against this function. - - Used against aggregate and window functions, - for database backends that support the "FILTER" clause. - - The expression:: - - func.count(1).filter(True) - - is shorthand for:: - - from sqlalchemy import funcfilter - funcfilter(func.count(1), True) - - .. seealso:: - - :ref:`tutorial_functions_within_group` - - in the :ref:`unified_tutorial` - - :class:`.FunctionFilter` - - :func:`.funcfilter` - - - """ - if not criterion: - return self - return FunctionFilter(self, *criterion) - - def as_comparison( - self, left_index: int, right_index: int - ) -> FunctionAsBinary: - """Interpret this expression as a boolean comparison between two - values. - - This method is used for an ORM use case described at - :ref:`relationship_custom_operator_sql_function`. - - A hypothetical SQL function "is_equal()" which compares to values - for equality would be written in the Core expression language as:: - - expr = func.is_equal("a", "b") - - If "is_equal()" above is comparing "a" and "b" for equality, the - :meth:`.FunctionElement.as_comparison` method would be invoked as:: - - expr = func.is_equal("a", "b").as_comparison(1, 2) - - Where above, the integer value "1" refers to the first argument of the - "is_equal()" function and the integer value "2" refers to the second. - - This would create a :class:`.BinaryExpression` that is equivalent to:: - - BinaryExpression("a", "b", operator=op.eq) - - However, at the SQL level it would still render as - "is_equal('a', 'b')". - - The ORM, when it loads a related object or collection, needs to be able - to manipulate the "left" and "right" sides of the ON clause of a JOIN - expression. The purpose of this method is to provide a SQL function - construct that can also supply this information to the ORM, when used - with the :paramref:`_orm.relationship.primaryjoin` parameter. The - return value is a containment object called :class:`.FunctionAsBinary`. - - An ORM example is as follows:: - - class Venue(Base): - __tablename__ = 'venue' - id = Column(Integer, primary_key=True) - name = Column(String) - - descendants = relationship( - "Venue", - primaryjoin=func.instr( - remote(foreign(name)), name + "/" - ).as_comparison(1, 2) == 1, - viewonly=True, - order_by=name - ) - - Above, the "Venue" class can load descendant "Venue" objects by - determining if the name of the parent Venue is contained within the - start of the hypothetical descendant value's name, e.g. "parent1" would - match up to "parent1/child1", but not to "parent2/child1". - - Possible use cases include the "materialized path" example given above, - as well as making use of special SQL functions such as geometric - functions to create join conditions. - - :param left_index: the integer 1-based index of the function argument - that serves as the "left" side of the expression. - :param right_index: the integer 1-based index of the function argument - that serves as the "right" side of the expression. - - .. versionadded:: 1.3 - - .. seealso:: - - :ref:`relationship_custom_operator_sql_function` - - example use within the ORM - - """ - return FunctionAsBinary(self, left_index, right_index) - - @property - def _from_objects(self) -> Any: - return self.clauses._from_objects - - def within_group_type( - self, within_group: WithinGroup[_S] - ) -> Optional[TypeEngine[_S]]: - """For types that define their return type as based on the criteria - within a WITHIN GROUP (ORDER BY) expression, called by the - :class:`.WithinGroup` construct. - - Returns None by default, in which case the function's normal ``.type`` - is used. - - """ - - return None - - def alias( - self, name: Optional[str] = None, joins_implicitly: bool = False - ) -> TableValuedAlias: - r"""Produce a :class:`_expression.Alias` construct against this - :class:`.FunctionElement`. - - .. tip:: - - The :meth:`_functions.FunctionElement.alias` method is part of the - mechanism by which "table valued" SQL functions are created. - However, most use cases are covered by higher level methods on - :class:`_functions.FunctionElement` including - :meth:`_functions.FunctionElement.table_valued`, and - :meth:`_functions.FunctionElement.column_valued`. - - This construct wraps the function in a named alias which - is suitable for the FROM clause, in the style accepted for example - by PostgreSQL. A column expression is also provided using the - special ``.column`` attribute, which may - be used to refer to the output of the function as a scalar value - in the columns or where clause, for a backend such as PostgreSQL. - - For a full table-valued expression, use the - :meth:`_functions.FunctionElement.table_valued` method first to - establish named columns. - - e.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import func, select, column - >>> data_view = func.unnest([1, 2, 3]).alias("data_view") - >>> print(select(data_view.column)) - {printsql}SELECT data_view - FROM unnest(:unnest_1) AS data_view - - The :meth:`_functions.FunctionElement.column_valued` method provides - a shortcut for the above pattern: - - .. sourcecode:: pycon+sql - - >>> data_view = func.unnest([1, 2, 3]).column_valued("data_view") - >>> print(select(data_view)) - {printsql}SELECT data_view - FROM unnest(:unnest_1) AS data_view - - .. versionadded:: 1.4.0b2 Added the ``.column`` accessor - - :param name: alias name, will be rendered as ``AS <name>`` in the - FROM clause - - :param joins_implicitly: when True, the table valued function may be - used in the FROM clause without any explicit JOIN to other tables - in the SQL query, and no "cartesian product" warning will be - generated. May be useful for SQL functions such as - ``func.json_each()``. - - .. versionadded:: 1.4.33 - - .. seealso:: - - :ref:`tutorial_functions_table_valued` - - in the :ref:`unified_tutorial` - - :meth:`_functions.FunctionElement.table_valued` - - :meth:`_functions.FunctionElement.scalar_table_valued` - - :meth:`_functions.FunctionElement.column_valued` - - - """ - - return TableValuedAlias._construct( - self, - name=name, - table_value_type=self.type, - joins_implicitly=joins_implicitly, - ) - - def select(self) -> Select[Tuple[_T]]: - """Produce a :func:`_expression.select` construct - against this :class:`.FunctionElement`. - - This is shorthand for:: - - s = select(function_element) - - """ - s: Select[Any] = Select(self) - if self._execution_options: - s = s.execution_options(**self._execution_options) - return s - - def _bind_param( - self, - operator: OperatorType, - obj: Any, - type_: Optional[TypeEngine[_T]] = None, - expanding: bool = False, - **kw: Any, - ) -> BindParameter[_T]: - return BindParameter( - None, - obj, - _compared_to_operator=operator, - _compared_to_type=self.type, - unique=True, - type_=type_, - expanding=expanding, - **kw, - ) - - def self_group(self, against: Optional[OperatorType] = None) -> ClauseElement: # type: ignore[override] # noqa E501 - # for the moment, we are parenthesizing all array-returning - # expressions against getitem. This may need to be made - # more portable if in the future we support other DBs - # besides postgresql. - if against is operators.getitem and isinstance( - self.type, sqltypes.ARRAY - ): - return Grouping(self) - else: - return super().self_group(against=against) - - @property - def entity_namespace(self) -> _EntityNamespace: - """overrides FromClause.entity_namespace as functions are generally - column expressions and not FromClauses. - - """ - # ideally functions would not be fromclauses but we failed to make - # this adjustment in 1.4 - return _entity_namespace(self.clause_expr) - - -class FunctionAsBinary(BinaryExpression[Any]): - _traverse_internals = [ - ("sql_function", InternalTraversal.dp_clauseelement), - ("left_index", InternalTraversal.dp_plain_obj), - ("right_index", InternalTraversal.dp_plain_obj), - ("modifiers", InternalTraversal.dp_plain_dict), - ] - - sql_function: FunctionElement[Any] - left_index: int - right_index: int - - def _gen_cache_key(self, anon_map: Any, bindparams: Any) -> Any: - return ColumnElement._gen_cache_key(self, anon_map, bindparams) - - def __init__( - self, fn: FunctionElement[Any], left_index: int, right_index: int - ): - self.sql_function = fn - self.left_index = left_index - self.right_index = right_index - - self.operator = operators.function_as_comparison_op - self.type = sqltypes.BOOLEANTYPE - self.negate = None - self._is_implicitly_boolean = True - self.modifiers = {} - - @property - def left_expr(self) -> ColumnElement[Any]: - return self.sql_function.clauses.clauses[self.left_index - 1] - - @left_expr.setter - def left_expr(self, value: ColumnElement[Any]) -> None: - self.sql_function.clauses.clauses[self.left_index - 1] = value - - @property - def right_expr(self) -> ColumnElement[Any]: - return self.sql_function.clauses.clauses[self.right_index - 1] - - @right_expr.setter - def right_expr(self, value: ColumnElement[Any]) -> None: - self.sql_function.clauses.clauses[self.right_index - 1] = value - - if not TYPE_CHECKING: - # mypy can't accommodate @property to replace an instance - # variable - - left = left_expr - right = right_expr - - -class ScalarFunctionColumn(NamedColumn[_T]): - __visit_name__ = "scalar_function_column" - - _traverse_internals = [ - ("name", InternalTraversal.dp_anon_name), - ("type", InternalTraversal.dp_type), - ("fn", InternalTraversal.dp_clauseelement), - ] - - is_literal = False - table = None - - def __init__( - self, - fn: FunctionElement[_T], - name: str, - type_: Optional[_TypeEngineArgument[_T]] = None, - ): - self.fn = fn - self.name = name - - # if type is None, we get NULLTYPE, which is our _T. But I don't - # know how to get the overloads to express that correctly - self.type = type_api.to_instance(type_) # type: ignore - - -class _FunctionGenerator: - """Generate SQL function expressions. - - :data:`.func` is a special object instance which generates SQL - functions based on name-based attributes, e.g.: - - .. sourcecode:: pycon+sql - - >>> print(func.count(1)) - {printsql}count(:param_1) - - The returned object is an instance of :class:`.Function`, and is a - column-oriented SQL element like any other, and is used in that way: - - .. sourcecode:: pycon+sql - - >>> print(select(func.count(table.c.id))) - {printsql}SELECT count(sometable.id) FROM sometable - - Any name can be given to :data:`.func`. If the function name is unknown to - SQLAlchemy, it will be rendered exactly as is. For common SQL functions - which SQLAlchemy is aware of, the name may be interpreted as a *generic - function* which will be compiled appropriately to the target database: - - .. sourcecode:: pycon+sql - - >>> print(func.current_timestamp()) - {printsql}CURRENT_TIMESTAMP - - To call functions which are present in dot-separated packages, - specify them in the same manner: - - .. sourcecode:: pycon+sql - - >>> print(func.stats.yield_curve(5, 10)) - {printsql}stats.yield_curve(:yield_curve_1, :yield_curve_2) - - SQLAlchemy can be made aware of the return type of functions to enable - type-specific lexical and result-based behavior. For example, to ensure - that a string-based function returns a Unicode value and is similarly - treated as a string in expressions, specify - :class:`~sqlalchemy.types.Unicode` as the type: - - .. sourcecode:: pycon+sql - - >>> print(func.my_string(u'hi', type_=Unicode) + ' ' + - ... func.my_string(u'there', type_=Unicode)) - {printsql}my_string(:my_string_1) || :my_string_2 || my_string(:my_string_3) - - The object returned by a :data:`.func` call is usually an instance of - :class:`.Function`. - This object meets the "column" interface, including comparison and labeling - functions. The object can also be passed the :meth:`~.Connectable.execute` - method of a :class:`_engine.Connection` or :class:`_engine.Engine`, - where it will be - wrapped inside of a SELECT statement first:: - - print(connection.execute(func.current_timestamp()).scalar()) - - In a few exception cases, the :data:`.func` accessor - will redirect a name to a built-in expression such as :func:`.cast` - or :func:`.extract`, as these names have well-known meaning - but are not exactly the same as "functions" from a SQLAlchemy - perspective. - - Functions which are interpreted as "generic" functions know how to - calculate their return type automatically. For a listing of known generic - functions, see :ref:`generic_functions`. - - .. note:: - - The :data:`.func` construct has only limited support for calling - standalone "stored procedures", especially those with special - parameterization concerns. - - See the section :ref:`stored_procedures` for details on how to use - the DBAPI-level ``callproc()`` method for fully traditional stored - procedures. - - .. seealso:: - - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` - - :class:`.Function` - - """ # noqa - - def __init__(self, **opts: Any): - self.__names: List[str] = [] - self.opts = opts - - def __getattr__(self, name: str) -> _FunctionGenerator: - # passthru __ attributes; fixes pydoc - if name.startswith("__"): - try: - return self.__dict__[name] # type: ignore - except KeyError: - raise AttributeError(name) - - elif name.endswith("_"): - name = name[0:-1] - f = _FunctionGenerator(**self.opts) - f.__names = list(self.__names) + [name] - return f - - @overload - def __call__( - self, *c: Any, type_: _TypeEngineArgument[_T], **kwargs: Any - ) -> Function[_T]: ... - - @overload - def __call__(self, *c: Any, **kwargs: Any) -> Function[Any]: ... - - def __call__(self, *c: Any, **kwargs: Any) -> Function[Any]: - o = self.opts.copy() - o.update(kwargs) - - tokens = len(self.__names) - - if tokens == 2: - package, fname = self.__names - elif tokens == 1: - package, fname = "_default", self.__names[0] - else: - package = None - - if package is not None: - func = _registry[package].get(fname.lower()) - if func is not None: - return func(*c, **o) - - return Function( - self.__names[-1], packagenames=tuple(self.__names[0:-1]), *c, **o - ) - - if TYPE_CHECKING: - # START GENERATED FUNCTION ACCESSORS - - # code within this block is **programmatically, - # statically generated** by tools/generate_sql_functions.py - - @property - def aggregate_strings(self) -> Type[aggregate_strings]: ... - - @property - def ansifunction(self) -> Type[AnsiFunction[Any]]: ... - - @property - def array_agg(self) -> Type[array_agg[Any]]: ... - - @property - def cast(self) -> Type[Cast[Any]]: ... - - @property - def char_length(self) -> Type[char_length]: ... - - # set ColumnElement[_T] as a separate overload, to appease mypy - # which seems to not want to accept _T from _ColumnExpressionArgument. - # this is even if all non-generic types are removed from it, so - # reasons remain unclear for why this does not work - - @overload - def coalesce( - self, - col: ColumnElement[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> coalesce[_T]: ... - - @overload - def coalesce( - self, - col: _ColumnExpressionArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> coalesce[_T]: ... - - @overload - def coalesce( - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> coalesce[_T]: ... - - def coalesce( - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> coalesce[_T]: ... - - @property - def concat(self) -> Type[concat]: ... - - @property - def count(self) -> Type[count]: ... - - @property - def cube(self) -> Type[cube[Any]]: ... - - @property - def cume_dist(self) -> Type[cume_dist]: ... - - @property - def current_date(self) -> Type[current_date]: ... - - @property - def current_time(self) -> Type[current_time]: ... - - @property - def current_timestamp(self) -> Type[current_timestamp]: ... - - @property - def current_user(self) -> Type[current_user]: ... - - @property - def dense_rank(self) -> Type[dense_rank]: ... - - @property - def extract(self) -> Type[Extract]: ... - - @property - def grouping_sets(self) -> Type[grouping_sets[Any]]: ... - - @property - def localtime(self) -> Type[localtime]: ... - - @property - def localtimestamp(self) -> Type[localtimestamp]: ... - - # set ColumnElement[_T] as a separate overload, to appease mypy - # which seems to not want to accept _T from _ColumnExpressionArgument. - # this is even if all non-generic types are removed from it, so - # reasons remain unclear for why this does not work - - @overload - def max( # noqa: A001 - self, - col: ColumnElement[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> max[_T]: ... - - @overload - def max( # noqa: A001 - self, - col: _ColumnExpressionArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> max[_T]: ... - - @overload - def max( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> max[_T]: ... - - def max( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> max[_T]: ... - - # set ColumnElement[_T] as a separate overload, to appease mypy - # which seems to not want to accept _T from _ColumnExpressionArgument. - # this is even if all non-generic types are removed from it, so - # reasons remain unclear for why this does not work - - @overload - def min( # noqa: A001 - self, - col: ColumnElement[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> min[_T]: ... - - @overload - def min( # noqa: A001 - self, - col: _ColumnExpressionArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> min[_T]: ... - - @overload - def min( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> min[_T]: ... - - def min( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> min[_T]: ... - - @property - def mode(self) -> Type[mode[Any]]: ... - - @property - def next_value(self) -> Type[next_value]: ... - - @property - def now(self) -> Type[now]: ... - - @property - def orderedsetagg(self) -> Type[OrderedSetAgg[Any]]: ... - - @property - def percent_rank(self) -> Type[percent_rank]: ... - - @property - def percentile_cont(self) -> Type[percentile_cont[Any]]: ... - - @property - def percentile_disc(self) -> Type[percentile_disc[Any]]: ... - - @property - def random(self) -> Type[random]: ... - - @property - def rank(self) -> Type[rank]: ... - - @property - def rollup(self) -> Type[rollup[Any]]: ... - - @property - def session_user(self) -> Type[session_user]: ... - - # set ColumnElement[_T] as a separate overload, to appease mypy - # which seems to not want to accept _T from _ColumnExpressionArgument. - # this is even if all non-generic types are removed from it, so - # reasons remain unclear for why this does not work - - @overload - def sum( # noqa: A001 - self, - col: ColumnElement[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> sum[_T]: ... - - @overload - def sum( # noqa: A001 - self, - col: _ColumnExpressionArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> sum[_T]: ... - - @overload - def sum( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> sum[_T]: ... - - def sum( # noqa: A001 - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ) -> sum[_T]: ... - - @property - def sysdate(self) -> Type[sysdate]: ... - - @property - def user(self) -> Type[user]: ... - - # END GENERATED FUNCTION ACCESSORS - - -func = _FunctionGenerator() -func.__doc__ = _FunctionGenerator.__doc__ - -modifier = _FunctionGenerator(group=False) - - -class Function(FunctionElement[_T]): - r"""Describe a named SQL function. - - The :class:`.Function` object is typically generated from the - :data:`.func` generation object. - - - :param \*clauses: list of column expressions that form the arguments - of the SQL function call. - - :param type\_: optional :class:`.TypeEngine` datatype object that will be - used as the return value of the column expression generated by this - function call. - - :param packagenames: a string which indicates package prefix names - to be prepended to the function name when the SQL is generated. - The :data:`.func` generator creates these when it is called using - dotted format, e.g.:: - - func.mypackage.some_function(col1, col2) - - .. seealso:: - - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` - - :data:`.func` - namespace which produces registered or ad-hoc - :class:`.Function` instances. - - :class:`.GenericFunction` - allows creation of registered function - types. - - """ - - __visit_name__ = "function" - - _traverse_internals = FunctionElement._traverse_internals + [ - ("packagenames", InternalTraversal.dp_plain_obj), - ("name", InternalTraversal.dp_string), - ("type", InternalTraversal.dp_type), - ] - - name: str - - identifier: str - - type: TypeEngine[_T] - """A :class:`_types.TypeEngine` object which refers to the SQL return - type represented by this SQL function. - - This datatype may be configured when generating a - :class:`_functions.Function` object by passing the - :paramref:`_functions.Function.type_` parameter, e.g.:: - - >>> select(func.lower("some VALUE", type_=String)) - - The small number of built-in classes of :class:`_functions.Function` come - with a built-in datatype that's appropriate to the class of function and - its arguments. For functions that aren't known, the type defaults to the - "null type". - - """ - - @overload - def __init__( - self, - name: str, - *clauses: _ColumnExpressionOrLiteralArgument[_T], - type_: None = ..., - packagenames: Optional[Tuple[str, ...]] = ..., - ): ... - - @overload - def __init__( - self, - name: str, - *clauses: _ColumnExpressionOrLiteralArgument[Any], - type_: _TypeEngineArgument[_T] = ..., - packagenames: Optional[Tuple[str, ...]] = ..., - ): ... - - def __init__( - self, - name: str, - *clauses: _ColumnExpressionOrLiteralArgument[Any], - type_: Optional[_TypeEngineArgument[_T]] = None, - packagenames: Optional[Tuple[str, ...]] = None, - ): - """Construct a :class:`.Function`. - - The :data:`.func` construct is normally used to construct - new :class:`.Function` instances. - - """ - self.packagenames = packagenames or () - self.name = name - - # if type is None, we get NULLTYPE, which is our _T. But I don't - # know how to get the overloads to express that correctly - self.type = type_api.to_instance(type_) # type: ignore - - FunctionElement.__init__(self, *clauses) - - def _bind_param( - self, - operator: OperatorType, - obj: Any, - type_: Optional[TypeEngine[_T]] = None, - expanding: bool = False, - **kw: Any, - ) -> BindParameter[_T]: - return BindParameter( - self.name, - obj, - _compared_to_operator=operator, - _compared_to_type=self.type, - type_=type_, - unique=True, - expanding=expanding, - **kw, - ) - - -class GenericFunction(Function[_T]): - """Define a 'generic' function. - - A generic function is a pre-established :class:`.Function` - class that is instantiated automatically when called - by name from the :data:`.func` attribute. Note that - calling any name from :data:`.func` has the effect that - a new :class:`.Function` instance is created automatically, - given that name. The primary use case for defining - a :class:`.GenericFunction` class is so that a function - of a particular name may be given a fixed return type. - It can also include custom argument parsing schemes as well - as additional methods. - - Subclasses of :class:`.GenericFunction` are automatically - registered under the name of the class. For - example, a user-defined function ``as_utc()`` would - be available immediately:: - - from sqlalchemy.sql.functions import GenericFunction - from sqlalchemy.types import DateTime - - class as_utc(GenericFunction): - type = DateTime() - inherit_cache = True - - print(select(func.as_utc())) - - User-defined generic functions can be organized into - packages by specifying the "package" attribute when defining - :class:`.GenericFunction`. Third party libraries - containing many functions may want to use this in order - to avoid name conflicts with other systems. For example, - if our ``as_utc()`` function were part of a package - "time":: - - class as_utc(GenericFunction): - type = DateTime() - package = "time" - inherit_cache = True - - The above function would be available from :data:`.func` - using the package name ``time``:: - - print(select(func.time.as_utc())) - - A final option is to allow the function to be accessed - from one name in :data:`.func` but to render as a different name. - The ``identifier`` attribute will override the name used to - access the function as loaded from :data:`.func`, but will retain - the usage of ``name`` as the rendered name:: - - class GeoBuffer(GenericFunction): - type = Geometry() - package = "geo" - name = "ST_Buffer" - identifier = "buffer" - inherit_cache = True - - The above function will render as follows: - - .. sourcecode:: pycon+sql - - >>> print(func.geo.buffer()) - {printsql}ST_Buffer() - - The name will be rendered as is, however without quoting unless the name - contains special characters that require quoting. To force quoting - on or off for the name, use the :class:`.sqlalchemy.sql.quoted_name` - construct:: - - from sqlalchemy.sql import quoted_name - - class GeoBuffer(GenericFunction): - type = Geometry() - package = "geo" - name = quoted_name("ST_Buffer", True) - identifier = "buffer" - inherit_cache = True - - The above function will render as: - - .. sourcecode:: pycon+sql - - >>> print(func.geo.buffer()) - {printsql}"ST_Buffer"() - - Type parameters for this class as a - `generic type <https://peps.python.org/pep-0484/#generics>`_ can be passed - and should match the type seen in a :class:`_engine.Result`. For example:: - - class as_utc(GenericFunction[datetime.datetime]): - type = DateTime() - inherit_cache = True - - The above indicates that the following expression returns a ``datetime`` - object:: - - connection.scalar(select(func.as_utc())) - - .. versionadded:: 1.3.13 The :class:`.quoted_name` construct is now - recognized for quoting when used with the "name" attribute of the - object, so that quoting can be forced on or off for the function - name. - - - """ - - coerce_arguments = True - inherit_cache = True - - _register: bool - - name = "GenericFunction" - - def __init_subclass__(cls) -> None: - if annotation.Annotated not in cls.__mro__: - cls._register_generic_function(cls.__name__, cls.__dict__) - super().__init_subclass__() - - @classmethod - def _register_generic_function( - cls, clsname: str, clsdict: Mapping[str, Any] - ) -> None: - cls.name = name = clsdict.get("name", clsname) - cls.identifier = identifier = clsdict.get("identifier", name) - package = clsdict.get("package", "_default") - # legacy - if "__return_type__" in clsdict: - cls.type = clsdict["__return_type__"] - - # Check _register attribute status - cls._register = getattr(cls, "_register", True) - - # Register the function if required - if cls._register: - register_function(identifier, cls, package) - else: - # Set _register to True to register child classes by default - cls._register = True - - def __init__( - self, *args: _ColumnExpressionOrLiteralArgument[Any], **kwargs: Any - ): - parsed_args = kwargs.pop("_parsed_args", None) - if parsed_args is None: - parsed_args = [ - coercions.expect( - roles.ExpressionElementRole, - c, - name=self.name, - apply_propagate_attrs=self, - ) - for c in args - ] - self._has_args = self._has_args or bool(parsed_args) - self.packagenames = () - - self.clause_expr = Grouping( - ClauseList( - operator=operators.comma_op, group_contents=True, *parsed_args - ) - ) - - self.type = type_api.to_instance( # type: ignore - kwargs.pop("type_", None) or getattr(self, "type", None) - ) - - -register_function("cast", Cast) # type: ignore -register_function("extract", Extract) # type: ignore - - -class next_value(GenericFunction[int]): - """Represent the 'next value', given a :class:`.Sequence` - as its single argument. - - Compiles into the appropriate function on each backend, - or will raise NotImplementedError if used on a backend - that does not provide support for sequences. - - """ - - type = sqltypes.Integer() - name = "next_value" - - _traverse_internals = [ - ("sequence", InternalTraversal.dp_named_ddl_element) - ] - - def __init__(self, seq: schema.Sequence, **kw: Any): - assert isinstance( - seq, schema.Sequence - ), "next_value() accepts a Sequence object as input." - self.sequence = seq - self.type = sqltypes.to_instance( # type: ignore - seq.data_type or getattr(self, "type", None) - ) - - def compare(self, other: Any, **kw: Any) -> bool: - return ( - isinstance(other, next_value) - and self.sequence.name == other.sequence.name - ) - - @property - def _from_objects(self) -> Any: - return [] - - -class AnsiFunction(GenericFunction[_T]): - """Define a function in "ansi" format, which doesn't render parenthesis.""" - - inherit_cache = True - - def __init__(self, *args: _ColumnExpressionArgument[Any], **kwargs: Any): - GenericFunction.__init__(self, *args, **kwargs) - - -class ReturnTypeFromArgs(GenericFunction[_T]): - """Define a function whose return type is the same as its arguments.""" - - inherit_cache = True - - # set ColumnElement[_T] as a separate overload, to appease mypy which seems - # to not want to accept _T from _ColumnExpressionArgument. this is even if - # all non-generic types are removed from it, so reasons remain unclear for - # why this does not work - - @overload - def __init__( - self, - col: ColumnElement[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ): ... - - @overload - def __init__( - self, - col: _ColumnExpressionArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ): ... - - @overload - def __init__( - self, - col: _ColumnExpressionOrLiteralArgument[_T], - *args: _ColumnExpressionOrLiteralArgument[Any], - **kwargs: Any, - ): ... - - def __init__( - self, *args: _ColumnExpressionOrLiteralArgument[Any], **kwargs: Any - ): - fn_args: Sequence[ColumnElement[Any]] = [ - coercions.expect( - roles.ExpressionElementRole, - c, - name=self.name, - apply_propagate_attrs=self, - ) - for c in args - ] - kwargs.setdefault("type_", _type_from_args(fn_args)) - kwargs["_parsed_args"] = fn_args - super().__init__(*fn_args, **kwargs) - - -class coalesce(ReturnTypeFromArgs[_T]): - _has_args = True - inherit_cache = True - - -class max(ReturnTypeFromArgs[_T]): # noqa: A001 - """The SQL MAX() aggregate function.""" - - inherit_cache = True - - -class min(ReturnTypeFromArgs[_T]): # noqa: A001 - """The SQL MIN() aggregate function.""" - - inherit_cache = True - - -class sum(ReturnTypeFromArgs[_T]): # noqa: A001 - """The SQL SUM() aggregate function.""" - - inherit_cache = True - - -class now(GenericFunction[datetime.datetime]): - """The SQL now() datetime function. - - SQLAlchemy dialects will usually render this particular function - in a backend-specific way, such as rendering it as ``CURRENT_TIMESTAMP``. - - """ - - type = sqltypes.DateTime() - inherit_cache = True - - -class concat(GenericFunction[str]): - """The SQL CONCAT() function, which concatenates strings. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> print(select(func.concat('a', 'b'))) - {printsql}SELECT concat(:concat_2, :concat_3) AS concat_1 - - String concatenation in SQLAlchemy is more commonly available using the - Python ``+`` operator with string datatypes, which will render a - backend-specific concatenation operator, such as : - - .. sourcecode:: pycon+sql - - >>> print(select(literal("a") + "b")) - {printsql}SELECT :param_1 || :param_2 AS anon_1 - - - """ - - type = sqltypes.String() - inherit_cache = True - - -class char_length(GenericFunction[int]): - """The CHAR_LENGTH() SQL function.""" - - type = sqltypes.Integer() - inherit_cache = True - - def __init__(self, arg: _ColumnExpressionArgument[str], **kw: Any): - # slight hack to limit to just one positional argument - # not sure why this one function has this special treatment - super().__init__(arg, **kw) - - -class random(GenericFunction[float]): - """The RANDOM() SQL function.""" - - _has_args = True - inherit_cache = True - - -class count(GenericFunction[int]): - r"""The ANSI COUNT aggregate function. With no arguments, - emits COUNT \*. - - E.g.:: - - from sqlalchemy import func - from sqlalchemy import select - from sqlalchemy import table, column - - my_table = table('some_table', column('id')) - - stmt = select(func.count()).select_from(my_table) - - Executing ``stmt`` would emit:: - - SELECT count(*) AS count_1 - FROM some_table - - - """ - - type = sqltypes.Integer() - inherit_cache = True - - def __init__( - self, - expression: Optional[_ColumnExpressionArgument[Any]] = None, - **kwargs: Any, - ): - if expression is None: - expression = literal_column("*") - super().__init__(expression, **kwargs) - - -class current_date(AnsiFunction[datetime.date]): - """The CURRENT_DATE() SQL function.""" - - type = sqltypes.Date() - inherit_cache = True - - -class current_time(AnsiFunction[datetime.time]): - """The CURRENT_TIME() SQL function.""" - - type = sqltypes.Time() - inherit_cache = True - - -class current_timestamp(AnsiFunction[datetime.datetime]): - """The CURRENT_TIMESTAMP() SQL function.""" - - type = sqltypes.DateTime() - inherit_cache = True - - -class current_user(AnsiFunction[str]): - """The CURRENT_USER() SQL function.""" - - type = sqltypes.String() - inherit_cache = True - - -class localtime(AnsiFunction[datetime.datetime]): - """The localtime() SQL function.""" - - type = sqltypes.DateTime() - inherit_cache = True - - -class localtimestamp(AnsiFunction[datetime.datetime]): - """The localtimestamp() SQL function.""" - - type = sqltypes.DateTime() - inherit_cache = True - - -class session_user(AnsiFunction[str]): - """The SESSION_USER() SQL function.""" - - type = sqltypes.String() - inherit_cache = True - - -class sysdate(AnsiFunction[datetime.datetime]): - """The SYSDATE() SQL function.""" - - type = sqltypes.DateTime() - inherit_cache = True - - -class user(AnsiFunction[str]): - """The USER() SQL function.""" - - type = sqltypes.String() - inherit_cache = True - - -class array_agg(GenericFunction[_T]): - """Support for the ARRAY_AGG function. - - The ``func.array_agg(expr)`` construct returns an expression of - type :class:`_types.ARRAY`. - - e.g.:: - - stmt = select(func.array_agg(table.c.values)[2:5]) - - .. seealso:: - - :func:`_postgresql.array_agg` - PostgreSQL-specific version that - returns :class:`_postgresql.ARRAY`, which has PG-specific operators - added. - - """ - - inherit_cache = True - - def __init__(self, *args: _ColumnExpressionArgument[Any], **kwargs: Any): - fn_args: Sequence[ColumnElement[Any]] = [ - coercions.expect( - roles.ExpressionElementRole, c, apply_propagate_attrs=self - ) - for c in args - ] - - default_array_type = kwargs.pop("_default_array_type", sqltypes.ARRAY) - if "type_" not in kwargs: - type_from_args = _type_from_args(fn_args) - if isinstance(type_from_args, sqltypes.ARRAY): - kwargs["type_"] = type_from_args - else: - kwargs["type_"] = default_array_type( - type_from_args, dimensions=1 - ) - kwargs["_parsed_args"] = fn_args - super().__init__(*fn_args, **kwargs) - - -class OrderedSetAgg(GenericFunction[_T]): - """Define a function where the return type is based on the sort - expression type as defined by the expression passed to the - :meth:`.FunctionElement.within_group` method.""" - - array_for_multi_clause = False - inherit_cache = True - - def within_group_type( - self, within_group: WithinGroup[Any] - ) -> TypeEngine[Any]: - func_clauses = cast(ClauseList, self.clause_expr.element) - order_by: Sequence[ColumnElement[Any]] = sqlutil.unwrap_order_by( - within_group.order_by - ) - if self.array_for_multi_clause and len(func_clauses.clauses) > 1: - return sqltypes.ARRAY(order_by[0].type) - else: - return order_by[0].type - - -class mode(OrderedSetAgg[_T]): - """Implement the ``mode`` ordered-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is the same as the sort expression. - - """ - - inherit_cache = True - - -class percentile_cont(OrderedSetAgg[_T]): - """Implement the ``percentile_cont`` ordered-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is the same as the sort expression, - or if the arguments are an array, an :class:`_types.ARRAY` of the sort - expression's type. - - """ - - array_for_multi_clause = True - inherit_cache = True - - -class percentile_disc(OrderedSetAgg[_T]): - """Implement the ``percentile_disc`` ordered-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is the same as the sort expression, - or if the arguments are an array, an :class:`_types.ARRAY` of the sort - expression's type. - - """ - - array_for_multi_clause = True - inherit_cache = True - - -class rank(GenericFunction[int]): - """Implement the ``rank`` hypothetical-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is :class:`.Integer`. - - """ - - type = sqltypes.Integer() - inherit_cache = True - - -class dense_rank(GenericFunction[int]): - """Implement the ``dense_rank`` hypothetical-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is :class:`.Integer`. - - """ - - type = sqltypes.Integer() - inherit_cache = True - - -class percent_rank(GenericFunction[decimal.Decimal]): - """Implement the ``percent_rank`` hypothetical-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is :class:`.Numeric`. - - """ - - type: sqltypes.Numeric[decimal.Decimal] = sqltypes.Numeric() - inherit_cache = True - - -class cume_dist(GenericFunction[decimal.Decimal]): - """Implement the ``cume_dist`` hypothetical-set aggregate function. - - This function must be used with the :meth:`.FunctionElement.within_group` - modifier to supply a sort expression to operate upon. - - The return type of this function is :class:`.Numeric`. - - """ - - type: sqltypes.Numeric[decimal.Decimal] = sqltypes.Numeric() - inherit_cache = True - - -class cube(GenericFunction[_T]): - r"""Implement the ``CUBE`` grouping operation. - - This function is used as part of the GROUP BY of a statement, - e.g. :meth:`_expression.Select.group_by`:: - - stmt = select( - func.sum(table.c.value), table.c.col_1, table.c.col_2 - ).group_by(func.cube(table.c.col_1, table.c.col_2)) - - .. versionadded:: 1.2 - - """ - - _has_args = True - inherit_cache = True - - -class rollup(GenericFunction[_T]): - r"""Implement the ``ROLLUP`` grouping operation. - - This function is used as part of the GROUP BY of a statement, - e.g. :meth:`_expression.Select.group_by`:: - - stmt = select( - func.sum(table.c.value), table.c.col_1, table.c.col_2 - ).group_by(func.rollup(table.c.col_1, table.c.col_2)) - - .. versionadded:: 1.2 - - """ - - _has_args = True - inherit_cache = True - - -class grouping_sets(GenericFunction[_T]): - r"""Implement the ``GROUPING SETS`` grouping operation. - - This function is used as part of the GROUP BY of a statement, - e.g. :meth:`_expression.Select.group_by`:: - - stmt = select( - func.sum(table.c.value), table.c.col_1, table.c.col_2 - ).group_by(func.grouping_sets(table.c.col_1, table.c.col_2)) - - In order to group by multiple sets, use the :func:`.tuple_` construct:: - - from sqlalchemy import tuple_ - - stmt = select( - func.sum(table.c.value), - table.c.col_1, table.c.col_2, - table.c.col_3 - ).group_by( - func.grouping_sets( - tuple_(table.c.col_1, table.c.col_2), - tuple_(table.c.value, table.c.col_3), - ) - ) - - - .. versionadded:: 1.2 - - """ - - _has_args = True - inherit_cache = True - - -class aggregate_strings(GenericFunction[str]): - """Implement a generic string aggregation function. - - This function will concatenate non-null values into a string and - separate the values by a delimiter. - - This function is compiled on a per-backend basis, into functions - such as ``group_concat()``, ``string_agg()``, or ``LISTAGG()``. - - e.g. Example usage with delimiter '.':: - - stmt = select(func.aggregate_strings(table.c.str_col, ".")) - - The return type of this function is :class:`.String`. - - .. versionadded: 2.0.21 - - """ - - type = sqltypes.String() - _has_args = True - inherit_cache = True - - def __init__(self, clause: _ColumnExpressionArgument[Any], separator: str): - super().__init__(clause, separator) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/lambdas.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/lambdas.py deleted file mode 100644 index 7a6b7b8..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/lambdas.py +++ /dev/null @@ -1,1449 +0,0 @@ -# sql/lambdas.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -from __future__ import annotations - -import collections.abc as collections_abc -import inspect -import itertools -import operator -import threading -import types -from types import CodeType -from typing import Any -from typing import Callable -from typing import cast -from typing import List -from typing import MutableMapping -from typing import Optional -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union -import weakref - -from . import cache_key as _cache_key -from . import coercions -from . import elements -from . import roles -from . import schema -from . import visitors -from .base import _clone -from .base import Executable -from .base import Options -from .cache_key import CacheConst -from .operators import ColumnOperators -from .. import exc -from .. import inspection -from .. import util -from ..util.typing import Literal - - -if TYPE_CHECKING: - from .elements import BindParameter - from .elements import ClauseElement - from .roles import SQLRole - from .visitors import _CloneCallableType - -_LambdaCacheType = MutableMapping[ - Tuple[Any, ...], Union["NonAnalyzedFunction", "AnalyzedFunction"] -] -_BoundParameterGetter = Callable[..., Any] - -_closure_per_cache_key: _LambdaCacheType = util.LRUCache(1000) - - -_LambdaType = Callable[[], Any] - -_AnyLambdaType = Callable[..., Any] - -_StmtLambdaType = Callable[[], Any] - -_E = TypeVar("_E", bound=Executable) -_StmtLambdaElementType = Callable[[_E], Any] - - -class LambdaOptions(Options): - enable_tracking = True - track_closure_variables = True - track_on: Optional[object] = None - global_track_bound_values = True - track_bound_values = True - lambda_cache: Optional[_LambdaCacheType] = None - - -def lambda_stmt( - lmb: _StmtLambdaType, - enable_tracking: bool = True, - track_closure_variables: bool = True, - track_on: Optional[object] = None, - global_track_bound_values: bool = True, - track_bound_values: bool = True, - lambda_cache: Optional[_LambdaCacheType] = None, -) -> StatementLambdaElement: - """Produce a SQL statement that is cached as a lambda. - - The Python code object within the lambda is scanned for both Python - literals that will become bound parameters as well as closure variables - that refer to Core or ORM constructs that may vary. The lambda itself - will be invoked only once per particular set of constructs detected. - - E.g.:: - - from sqlalchemy import lambda_stmt - - stmt = lambda_stmt(lambda: table.select()) - stmt += lambda s: s.where(table.c.id == 5) - - result = connection.execute(stmt) - - The object returned is an instance of :class:`_sql.StatementLambdaElement`. - - .. versionadded:: 1.4 - - :param lmb: a Python function, typically a lambda, which takes no arguments - and returns a SQL expression construct - :param enable_tracking: when False, all scanning of the given lambda for - changes in closure variables or bound parameters is disabled. Use for - a lambda that produces the identical results in all cases with no - parameterization. - :param track_closure_variables: when False, changes in closure variables - within the lambda will not be scanned. Use for a lambda where the - state of its closure variables will never change the SQL structure - returned by the lambda. - :param track_bound_values: when False, bound parameter tracking will - be disabled for the given lambda. Use for a lambda that either does - not produce any bound values, or where the initial bound values never - change. - :param global_track_bound_values: when False, bound parameter tracking - will be disabled for the entire statement including additional links - added via the :meth:`_sql.StatementLambdaElement.add_criteria` method. - :param lambda_cache: a dictionary or other mapping-like object where - information about the lambda's Python code as well as the tracked closure - variables in the lambda itself will be stored. Defaults - to a global LRU cache. This cache is independent of the "compiled_cache" - used by the :class:`_engine.Connection` object. - - .. seealso:: - - :ref:`engine_lambda_caching` - - - """ - - return StatementLambdaElement( - lmb, - roles.StatementRole, - LambdaOptions( - enable_tracking=enable_tracking, - track_on=track_on, - track_closure_variables=track_closure_variables, - global_track_bound_values=global_track_bound_values, - track_bound_values=track_bound_values, - lambda_cache=lambda_cache, - ), - ) - - -class LambdaElement(elements.ClauseElement): - """A SQL construct where the state is stored as an un-invoked lambda. - - The :class:`_sql.LambdaElement` is produced transparently whenever - passing lambda expressions into SQL constructs, such as:: - - stmt = select(table).where(lambda: table.c.col == parameter) - - The :class:`_sql.LambdaElement` is the base of the - :class:`_sql.StatementLambdaElement` which represents a full statement - within a lambda. - - .. versionadded:: 1.4 - - .. seealso:: - - :ref:`engine_lambda_caching` - - """ - - __visit_name__ = "lambda_element" - - _is_lambda_element = True - - _traverse_internals = [ - ("_resolved", visitors.InternalTraversal.dp_clauseelement) - ] - - _transforms: Tuple[_CloneCallableType, ...] = () - - _resolved_bindparams: List[BindParameter[Any]] - parent_lambda: Optional[StatementLambdaElement] = None - closure_cache_key: Union[Tuple[Any, ...], Literal[CacheConst.NO_CACHE]] - role: Type[SQLRole] - _rec: Union[AnalyzedFunction, NonAnalyzedFunction] - fn: _AnyLambdaType - tracker_key: Tuple[CodeType, ...] - - def __repr__(self): - return "%s(%r)" % ( - self.__class__.__name__, - self.fn.__code__, - ) - - def __init__( - self, - fn: _LambdaType, - role: Type[SQLRole], - opts: Union[Type[LambdaOptions], LambdaOptions] = LambdaOptions, - apply_propagate_attrs: Optional[ClauseElement] = None, - ): - self.fn = fn - self.role = role - self.tracker_key = (fn.__code__,) - self.opts = opts - - if apply_propagate_attrs is None and (role is roles.StatementRole): - apply_propagate_attrs = self - - rec = self._retrieve_tracker_rec(fn, apply_propagate_attrs, opts) - - if apply_propagate_attrs is not None: - propagate_attrs = rec.propagate_attrs - if propagate_attrs: - apply_propagate_attrs._propagate_attrs = propagate_attrs - - def _retrieve_tracker_rec(self, fn, apply_propagate_attrs, opts): - lambda_cache = opts.lambda_cache - if lambda_cache is None: - lambda_cache = _closure_per_cache_key - - tracker_key = self.tracker_key - - fn = self.fn - closure = fn.__closure__ - tracker = AnalyzedCode.get( - fn, - self, - opts, - ) - - bindparams: List[BindParameter[Any]] - self._resolved_bindparams = bindparams = [] - - if self.parent_lambda is not None: - parent_closure_cache_key = self.parent_lambda.closure_cache_key - else: - parent_closure_cache_key = () - - cache_key: Union[Tuple[Any, ...], Literal[CacheConst.NO_CACHE]] - - if parent_closure_cache_key is not _cache_key.NO_CACHE: - anon_map = visitors.anon_map() - cache_key = tuple( - [ - getter(closure, opts, anon_map, bindparams) - for getter in tracker.closure_trackers - ] - ) - - if _cache_key.NO_CACHE not in anon_map: - cache_key = parent_closure_cache_key + cache_key - - self.closure_cache_key = cache_key - - try: - rec = lambda_cache[tracker_key + cache_key] - except KeyError: - rec = None - else: - cache_key = _cache_key.NO_CACHE - rec = None - - else: - cache_key = _cache_key.NO_CACHE - rec = None - - self.closure_cache_key = cache_key - - if rec is None: - if cache_key is not _cache_key.NO_CACHE: - with AnalyzedCode._generation_mutex: - key = tracker_key + cache_key - if key not in lambda_cache: - rec = AnalyzedFunction( - tracker, self, apply_propagate_attrs, fn - ) - rec.closure_bindparams = bindparams - lambda_cache[key] = rec - else: - rec = lambda_cache[key] - else: - rec = NonAnalyzedFunction(self._invoke_user_fn(fn)) - - else: - bindparams[:] = [ - orig_bind._with_value(new_bind.value, maintain_key=True) - for orig_bind, new_bind in zip( - rec.closure_bindparams, bindparams - ) - ] - - self._rec = rec - - if cache_key is not _cache_key.NO_CACHE: - if self.parent_lambda is not None: - bindparams[:0] = self.parent_lambda._resolved_bindparams - - lambda_element: Optional[LambdaElement] = self - while lambda_element is not None: - rec = lambda_element._rec - if rec.bindparam_trackers: - tracker_instrumented_fn = rec.tracker_instrumented_fn - for tracker in rec.bindparam_trackers: - tracker( - lambda_element.fn, - tracker_instrumented_fn, - bindparams, - ) - lambda_element = lambda_element.parent_lambda - - return rec - - def __getattr__(self, key): - return getattr(self._rec.expected_expr, key) - - @property - def _is_sequence(self): - return self._rec.is_sequence - - @property - def _select_iterable(self): - if self._is_sequence: - return itertools.chain.from_iterable( - [element._select_iterable for element in self._resolved] - ) - - else: - return self._resolved._select_iterable - - @property - def _from_objects(self): - if self._is_sequence: - return itertools.chain.from_iterable( - [element._from_objects for element in self._resolved] - ) - - else: - return self._resolved._from_objects - - def _param_dict(self): - return {b.key: b.value for b in self._resolved_bindparams} - - def _setup_binds_for_tracked_expr(self, expr): - bindparam_lookup = {b.key: b for b in self._resolved_bindparams} - - def replace( - element: Optional[visitors.ExternallyTraversible], **kw: Any - ) -> Optional[visitors.ExternallyTraversible]: - if isinstance(element, elements.BindParameter): - if element.key in bindparam_lookup: - bind = bindparam_lookup[element.key] - if element.expanding: - bind.expanding = True - bind.expand_op = element.expand_op - bind.type = element.type - return bind - - return None - - if self._rec.is_sequence: - expr = [ - visitors.replacement_traverse(sub_expr, {}, replace) - for sub_expr in expr - ] - elif getattr(expr, "is_clause_element", False): - expr = visitors.replacement_traverse(expr, {}, replace) - - return expr - - def _copy_internals( - self, - clone: _CloneCallableType = _clone, - deferred_copy_internals: Optional[_CloneCallableType] = None, - **kw: Any, - ) -> None: - # TODO: this needs A LOT of tests - self._resolved = clone( - self._resolved, - deferred_copy_internals=deferred_copy_internals, - **kw, - ) - - @util.memoized_property - def _resolved(self): - expr = self._rec.expected_expr - - if self._resolved_bindparams: - expr = self._setup_binds_for_tracked_expr(expr) - - return expr - - def _gen_cache_key(self, anon_map, bindparams): - if self.closure_cache_key is _cache_key.NO_CACHE: - anon_map[_cache_key.NO_CACHE] = True - return None - - cache_key = ( - self.fn.__code__, - self.__class__, - ) + self.closure_cache_key - - parent = self.parent_lambda - - while parent is not None: - assert parent.closure_cache_key is not CacheConst.NO_CACHE - parent_closure_cache_key: Tuple[Any, ...] = ( - parent.closure_cache_key - ) - - cache_key = ( - (parent.fn.__code__,) + parent_closure_cache_key + cache_key - ) - - parent = parent.parent_lambda - - if self._resolved_bindparams: - bindparams.extend(self._resolved_bindparams) - return cache_key - - def _invoke_user_fn(self, fn: _AnyLambdaType, *arg: Any) -> ClauseElement: - return fn() # type: ignore[no-any-return] - - -class DeferredLambdaElement(LambdaElement): - """A LambdaElement where the lambda accepts arguments and is - invoked within the compile phase with special context. - - This lambda doesn't normally produce its real SQL expression outside of the - compile phase. It is passed a fixed set of initial arguments - so that it can generate a sample expression. - - """ - - def __init__( - self, - fn: _AnyLambdaType, - role: Type[roles.SQLRole], - opts: Union[Type[LambdaOptions], LambdaOptions] = LambdaOptions, - lambda_args: Tuple[Any, ...] = (), - ): - self.lambda_args = lambda_args - super().__init__(fn, role, opts) - - def _invoke_user_fn(self, fn, *arg): - return fn(*self.lambda_args) - - def _resolve_with_args(self, *lambda_args: Any) -> ClauseElement: - assert isinstance(self._rec, AnalyzedFunction) - tracker_fn = self._rec.tracker_instrumented_fn - expr = tracker_fn(*lambda_args) - - expr = coercions.expect(self.role, expr) - - expr = self._setup_binds_for_tracked_expr(expr) - - # this validation is getting very close, but not quite, to achieving - # #5767. The problem is if the base lambda uses an unnamed column - # as is very common with mixins, the parameter name is different - # and it produces a false positive; that is, for the documented case - # that is exactly what people will be doing, it doesn't work, so - # I'm not really sure how to handle this right now. - # expected_binds = [ - # b._orig_key - # for b in self._rec.expr._generate_cache_key()[1] - # if b.required - # ] - # got_binds = [ - # b._orig_key for b in expr._generate_cache_key()[1] if b.required - # ] - # if expected_binds != got_binds: - # raise exc.InvalidRequestError( - # "Lambda callable at %s produced a different set of bound " - # "parameters than its original run: %s" - # % (self.fn.__code__, ", ".join(got_binds)) - # ) - - # TODO: TEST TEST TEST, this is very out there - for deferred_copy_internals in self._transforms: - expr = deferred_copy_internals(expr) - - return expr # type: ignore - - def _copy_internals( - self, clone=_clone, deferred_copy_internals=None, **kw - ): - super()._copy_internals( - clone=clone, - deferred_copy_internals=deferred_copy_internals, # **kw - opts=kw, - ) - - # TODO: A LOT A LOT of tests. for _resolve_with_args, we don't know - # our expression yet. so hold onto the replacement - if deferred_copy_internals: - self._transforms += (deferred_copy_internals,) - - -class StatementLambdaElement( - roles.AllowsLambdaRole, LambdaElement, Executable -): - """Represent a composable SQL statement as a :class:`_sql.LambdaElement`. - - The :class:`_sql.StatementLambdaElement` is constructed using the - :func:`_sql.lambda_stmt` function:: - - - from sqlalchemy import lambda_stmt - - stmt = lambda_stmt(lambda: select(table)) - - Once constructed, additional criteria can be built onto the statement - by adding subsequent lambdas, which accept the existing statement - object as a single parameter:: - - stmt += lambda s: s.where(table.c.col == parameter) - - - .. versionadded:: 1.4 - - .. seealso:: - - :ref:`engine_lambda_caching` - - """ - - if TYPE_CHECKING: - - def __init__( - self, - fn: _StmtLambdaType, - role: Type[SQLRole], - opts: Union[Type[LambdaOptions], LambdaOptions] = LambdaOptions, - apply_propagate_attrs: Optional[ClauseElement] = None, - ): ... - - def __add__( - self, other: _StmtLambdaElementType[Any] - ) -> StatementLambdaElement: - return self.add_criteria(other) - - def add_criteria( - self, - other: _StmtLambdaElementType[Any], - enable_tracking: bool = True, - track_on: Optional[Any] = None, - track_closure_variables: bool = True, - track_bound_values: bool = True, - ) -> StatementLambdaElement: - """Add new criteria to this :class:`_sql.StatementLambdaElement`. - - E.g.:: - - >>> def my_stmt(parameter): - ... stmt = lambda_stmt( - ... lambda: select(table.c.x, table.c.y), - ... ) - ... stmt = stmt.add_criteria( - ... lambda: table.c.x > parameter - ... ) - ... return stmt - - The :meth:`_sql.StatementLambdaElement.add_criteria` method is - equivalent to using the Python addition operator to add a new - lambda, except that additional arguments may be added including - ``track_closure_values`` and ``track_on``:: - - >>> def my_stmt(self, foo): - ... stmt = lambda_stmt( - ... lambda: select(func.max(foo.x, foo.y)), - ... track_closure_variables=False - ... ) - ... stmt = stmt.add_criteria( - ... lambda: self.where_criteria, - ... track_on=[self] - ... ) - ... return stmt - - See :func:`_sql.lambda_stmt` for a description of the parameters - accepted. - - """ - - opts = self.opts + dict( - enable_tracking=enable_tracking, - track_closure_variables=track_closure_variables, - global_track_bound_values=self.opts.global_track_bound_values, - track_on=track_on, - track_bound_values=track_bound_values, - ) - - return LinkedLambdaElement(other, parent_lambda=self, opts=opts) - - def _execute_on_connection( - self, connection, distilled_params, execution_options - ): - if TYPE_CHECKING: - assert isinstance(self._rec.expected_expr, ClauseElement) - if self._rec.expected_expr.supports_execution: - return connection._execute_clauseelement( - self, distilled_params, execution_options - ) - else: - raise exc.ObjectNotExecutableError(self) - - @property - def _proxied(self) -> Any: - return self._rec_expected_expr - - @property - def _with_options(self): - return self._proxied._with_options - - @property - def _effective_plugin_target(self): - return self._proxied._effective_plugin_target - - @property - def _execution_options(self): - return self._proxied._execution_options - - @property - def _all_selected_columns(self): - return self._proxied._all_selected_columns - - @property - def is_select(self): - return self._proxied.is_select - - @property - def is_update(self): - return self._proxied.is_update - - @property - def is_insert(self): - return self._proxied.is_insert - - @property - def is_text(self): - return self._proxied.is_text - - @property - def is_delete(self): - return self._proxied.is_delete - - @property - def is_dml(self): - return self._proxied.is_dml - - def spoil(self) -> NullLambdaStatement: - """Return a new :class:`.StatementLambdaElement` that will run - all lambdas unconditionally each time. - - """ - return NullLambdaStatement(self.fn()) - - -class NullLambdaStatement(roles.AllowsLambdaRole, elements.ClauseElement): - """Provides the :class:`.StatementLambdaElement` API but does not - cache or analyze lambdas. - - the lambdas are instead invoked immediately. - - The intended use is to isolate issues that may arise when using - lambda statements. - - """ - - __visit_name__ = "lambda_element" - - _is_lambda_element = True - - _traverse_internals = [ - ("_resolved", visitors.InternalTraversal.dp_clauseelement) - ] - - def __init__(self, statement): - self._resolved = statement - self._propagate_attrs = statement._propagate_attrs - - def __getattr__(self, key): - return getattr(self._resolved, key) - - def __add__(self, other): - statement = other(self._resolved) - - return NullLambdaStatement(statement) - - def add_criteria(self, other, **kw): - statement = other(self._resolved) - - return NullLambdaStatement(statement) - - def _execute_on_connection( - self, connection, distilled_params, execution_options - ): - if self._resolved.supports_execution: - return connection._execute_clauseelement( - self, distilled_params, execution_options - ) - else: - raise exc.ObjectNotExecutableError(self) - - -class LinkedLambdaElement(StatementLambdaElement): - """Represent subsequent links of a :class:`.StatementLambdaElement`.""" - - parent_lambda: StatementLambdaElement - - def __init__( - self, - fn: _StmtLambdaElementType[Any], - parent_lambda: StatementLambdaElement, - opts: Union[Type[LambdaOptions], LambdaOptions], - ): - self.opts = opts - self.fn = fn - self.parent_lambda = parent_lambda - - self.tracker_key = parent_lambda.tracker_key + (fn.__code__,) - self._retrieve_tracker_rec(fn, self, opts) - self._propagate_attrs = parent_lambda._propagate_attrs - - def _invoke_user_fn(self, fn, *arg): - return fn(self.parent_lambda._resolved) - - -class AnalyzedCode: - __slots__ = ( - "track_closure_variables", - "track_bound_values", - "bindparam_trackers", - "closure_trackers", - "build_py_wrappers", - ) - _fns: weakref.WeakKeyDictionary[CodeType, AnalyzedCode] = ( - weakref.WeakKeyDictionary() - ) - - _generation_mutex = threading.RLock() - - @classmethod - def get(cls, fn, lambda_element, lambda_kw, **kw): - try: - # TODO: validate kw haven't changed? - return cls._fns[fn.__code__] - except KeyError: - pass - - with cls._generation_mutex: - # check for other thread already created object - if fn.__code__ in cls._fns: - return cls._fns[fn.__code__] - - analyzed: AnalyzedCode - cls._fns[fn.__code__] = analyzed = AnalyzedCode( - fn, lambda_element, lambda_kw, **kw - ) - return analyzed - - def __init__(self, fn, lambda_element, opts): - if inspect.ismethod(fn): - raise exc.ArgumentError( - "Method %s may not be passed as a SQL expression" % fn - ) - closure = fn.__closure__ - - self.track_bound_values = ( - opts.track_bound_values and opts.global_track_bound_values - ) - enable_tracking = opts.enable_tracking - track_on = opts.track_on - track_closure_variables = opts.track_closure_variables - - self.track_closure_variables = track_closure_variables and not track_on - - # a list of callables generated from _bound_parameter_getter_* - # functions. Each of these uses a PyWrapper object to retrieve - # a parameter value - self.bindparam_trackers = [] - - # a list of callables generated from _cache_key_getter_* functions - # these callables work to generate a cache key for the lambda - # based on what's inside its closure variables. - self.closure_trackers = [] - - self.build_py_wrappers = [] - - if enable_tracking: - if track_on: - self._init_track_on(track_on) - - self._init_globals(fn) - - if closure: - self._init_closure(fn) - - self._setup_additional_closure_trackers(fn, lambda_element, opts) - - def _init_track_on(self, track_on): - self.closure_trackers.extend( - self._cache_key_getter_track_on(idx, elem) - for idx, elem in enumerate(track_on) - ) - - def _init_globals(self, fn): - build_py_wrappers = self.build_py_wrappers - bindparam_trackers = self.bindparam_trackers - track_bound_values = self.track_bound_values - - for name in fn.__code__.co_names: - if name not in fn.__globals__: - continue - - _bound_value = self._roll_down_to_literal(fn.__globals__[name]) - - if coercions._deep_is_literal(_bound_value): - build_py_wrappers.append((name, None)) - if track_bound_values: - bindparam_trackers.append( - self._bound_parameter_getter_func_globals(name) - ) - - def _init_closure(self, fn): - build_py_wrappers = self.build_py_wrappers - closure = fn.__closure__ - - track_bound_values = self.track_bound_values - track_closure_variables = self.track_closure_variables - bindparam_trackers = self.bindparam_trackers - closure_trackers = self.closure_trackers - - for closure_index, (fv, cell) in enumerate( - zip(fn.__code__.co_freevars, closure) - ): - _bound_value = self._roll_down_to_literal(cell.cell_contents) - - if coercions._deep_is_literal(_bound_value): - build_py_wrappers.append((fv, closure_index)) - if track_bound_values: - bindparam_trackers.append( - self._bound_parameter_getter_func_closure( - fv, closure_index - ) - ) - else: - # for normal cell contents, add them to a list that - # we can compare later when we get new lambdas. if - # any identities have changed, then we will - # recalculate the whole lambda and run it again. - - if track_closure_variables: - closure_trackers.append( - self._cache_key_getter_closure_variable( - fn, fv, closure_index, cell.cell_contents - ) - ) - - def _setup_additional_closure_trackers(self, fn, lambda_element, opts): - # an additional step is to actually run the function, then - # go through the PyWrapper objects that were set up to catch a bound - # parameter. then if they *didn't* make a param, oh they're another - # object in the closure we have to track for our cache key. so - # create trackers to catch those. - - analyzed_function = AnalyzedFunction( - self, - lambda_element, - None, - fn, - ) - - closure_trackers = self.closure_trackers - - for pywrapper in analyzed_function.closure_pywrappers: - if not pywrapper._sa__has_param: - closure_trackers.append( - self._cache_key_getter_tracked_literal(fn, pywrapper) - ) - - @classmethod - def _roll_down_to_literal(cls, element): - is_clause_element = hasattr(element, "__clause_element__") - - if is_clause_element: - while not isinstance( - element, (elements.ClauseElement, schema.SchemaItem, type) - ): - try: - element = element.__clause_element__() - except AttributeError: - break - - if not is_clause_element: - insp = inspection.inspect(element, raiseerr=False) - if insp is not None: - try: - return insp.__clause_element__() - except AttributeError: - return insp - - # TODO: should we coerce consts None/True/False here? - return element - else: - return element - - def _bound_parameter_getter_func_globals(self, name): - """Return a getter that will extend a list of bound parameters - with new entries from the ``__globals__`` collection of a particular - lambda. - - """ - - def extract_parameter_value( - current_fn, tracker_instrumented_fn, result - ): - wrapper = tracker_instrumented_fn.__globals__[name] - object.__getattribute__(wrapper, "_extract_bound_parameters")( - current_fn.__globals__[name], result - ) - - return extract_parameter_value - - def _bound_parameter_getter_func_closure(self, name, closure_index): - """Return a getter that will extend a list of bound parameters - with new entries from the ``__closure__`` collection of a particular - lambda. - - """ - - def extract_parameter_value( - current_fn, tracker_instrumented_fn, result - ): - wrapper = tracker_instrumented_fn.__closure__[ - closure_index - ].cell_contents - object.__getattribute__(wrapper, "_extract_bound_parameters")( - current_fn.__closure__[closure_index].cell_contents, result - ) - - return extract_parameter_value - - def _cache_key_getter_track_on(self, idx, elem): - """Return a getter that will extend a cache key with new entries - from the "track_on" parameter passed to a :class:`.LambdaElement`. - - """ - - if isinstance(elem, tuple): - # tuple must contain hascachekey elements - def get(closure, opts, anon_map, bindparams): - return tuple( - tup_elem._gen_cache_key(anon_map, bindparams) - for tup_elem in opts.track_on[idx] - ) - - elif isinstance(elem, _cache_key.HasCacheKey): - - def get(closure, opts, anon_map, bindparams): - return opts.track_on[idx]._gen_cache_key(anon_map, bindparams) - - else: - - def get(closure, opts, anon_map, bindparams): - return opts.track_on[idx] - - return get - - def _cache_key_getter_closure_variable( - self, - fn, - variable_name, - idx, - cell_contents, - use_clause_element=False, - use_inspect=False, - ): - """Return a getter that will extend a cache key with new entries - from the ``__closure__`` collection of a particular lambda. - - """ - - if isinstance(cell_contents, _cache_key.HasCacheKey): - - def get(closure, opts, anon_map, bindparams): - obj = closure[idx].cell_contents - if use_inspect: - obj = inspection.inspect(obj) - elif use_clause_element: - while hasattr(obj, "__clause_element__"): - if not getattr(obj, "is_clause_element", False): - obj = obj.__clause_element__() - - return obj._gen_cache_key(anon_map, bindparams) - - elif isinstance(cell_contents, types.FunctionType): - - def get(closure, opts, anon_map, bindparams): - return closure[idx].cell_contents.__code__ - - elif isinstance(cell_contents, collections_abc.Sequence): - - def get(closure, opts, anon_map, bindparams): - contents = closure[idx].cell_contents - - try: - return tuple( - elem._gen_cache_key(anon_map, bindparams) - for elem in contents - ) - except AttributeError as ae: - self._raise_for_uncacheable_closure_variable( - variable_name, fn, from_=ae - ) - - else: - # if the object is a mapped class or aliased class, or some - # other object in the ORM realm of things like that, imitate - # the logic used in coercions.expect() to roll it down to the - # SQL element - element = cell_contents - is_clause_element = False - while hasattr(element, "__clause_element__"): - is_clause_element = True - if not getattr(element, "is_clause_element", False): - element = element.__clause_element__() - else: - break - - if not is_clause_element: - insp = inspection.inspect(element, raiseerr=False) - if insp is not None: - return self._cache_key_getter_closure_variable( - fn, variable_name, idx, insp, use_inspect=True - ) - else: - return self._cache_key_getter_closure_variable( - fn, variable_name, idx, element, use_clause_element=True - ) - - self._raise_for_uncacheable_closure_variable(variable_name, fn) - - return get - - def _raise_for_uncacheable_closure_variable( - self, variable_name, fn, from_=None - ): - raise exc.InvalidRequestError( - "Closure variable named '%s' inside of lambda callable %s " - "does not refer to a cacheable SQL element, and also does not " - "appear to be serving as a SQL literal bound value based on " - "the default " - "SQL expression returned by the function. This variable " - "needs to remain outside the scope of a SQL-generating lambda " - "so that a proper cache key may be generated from the " - "lambda's state. Evaluate this variable outside of the " - "lambda, set track_on=[<elements>] to explicitly select " - "closure elements to track, or set " - "track_closure_variables=False to exclude " - "closure variables from being part of the cache key." - % (variable_name, fn.__code__), - ) from from_ - - def _cache_key_getter_tracked_literal(self, fn, pytracker): - """Return a getter that will extend a cache key with new entries - from the ``__closure__`` collection of a particular lambda. - - this getter differs from _cache_key_getter_closure_variable - in that these are detected after the function is run, and PyWrapper - objects have recorded that a particular literal value is in fact - not being interpreted as a bound parameter. - - """ - - elem = pytracker._sa__to_evaluate - closure_index = pytracker._sa__closure_index - variable_name = pytracker._sa__name - - return self._cache_key_getter_closure_variable( - fn, variable_name, closure_index, elem - ) - - -class NonAnalyzedFunction: - __slots__ = ("expr",) - - closure_bindparams: Optional[List[BindParameter[Any]]] = None - bindparam_trackers: Optional[List[_BoundParameterGetter]] = None - - is_sequence = False - - expr: ClauseElement - - def __init__(self, expr: ClauseElement): - self.expr = expr - - @property - def expected_expr(self) -> ClauseElement: - return self.expr - - -class AnalyzedFunction: - __slots__ = ( - "analyzed_code", - "fn", - "closure_pywrappers", - "tracker_instrumented_fn", - "expr", - "bindparam_trackers", - "expected_expr", - "is_sequence", - "propagate_attrs", - "closure_bindparams", - ) - - closure_bindparams: Optional[List[BindParameter[Any]]] - expected_expr: Union[ClauseElement, List[ClauseElement]] - bindparam_trackers: Optional[List[_BoundParameterGetter]] - - def __init__( - self, - analyzed_code, - lambda_element, - apply_propagate_attrs, - fn, - ): - self.analyzed_code = analyzed_code - self.fn = fn - - self.bindparam_trackers = analyzed_code.bindparam_trackers - - self._instrument_and_run_function(lambda_element) - - self._coerce_expression(lambda_element, apply_propagate_attrs) - - def _instrument_and_run_function(self, lambda_element): - analyzed_code = self.analyzed_code - - fn = self.fn - self.closure_pywrappers = closure_pywrappers = [] - - build_py_wrappers = analyzed_code.build_py_wrappers - - if not build_py_wrappers: - self.tracker_instrumented_fn = tracker_instrumented_fn = fn - self.expr = lambda_element._invoke_user_fn(tracker_instrumented_fn) - else: - track_closure_variables = analyzed_code.track_closure_variables - closure = fn.__closure__ - - # will form the __closure__ of the function when we rebuild it - if closure: - new_closure = { - fv: cell.cell_contents - for fv, cell in zip(fn.__code__.co_freevars, closure) - } - else: - new_closure = {} - - # will form the __globals__ of the function when we rebuild it - new_globals = fn.__globals__.copy() - - for name, closure_index in build_py_wrappers: - if closure_index is not None: - value = closure[closure_index].cell_contents - new_closure[name] = bind = PyWrapper( - fn, - name, - value, - closure_index=closure_index, - track_bound_values=( - self.analyzed_code.track_bound_values - ), - ) - if track_closure_variables: - closure_pywrappers.append(bind) - else: - value = fn.__globals__[name] - new_globals[name] = bind = PyWrapper(fn, name, value) - - # rewrite the original fn. things that look like they will - # become bound parameters are wrapped in a PyWrapper. - self.tracker_instrumented_fn = tracker_instrumented_fn = ( - self._rewrite_code_obj( - fn, - [new_closure[name] for name in fn.__code__.co_freevars], - new_globals, - ) - ) - - # now invoke the function. This will give us a new SQL - # expression, but all the places that there would be a bound - # parameter, the PyWrapper in its place will give us a bind - # with a predictable name we can match up later. - - # additionally, each PyWrapper will log that it did in fact - # create a parameter, otherwise, it's some kind of Python - # object in the closure and we want to track that, to make - # sure it doesn't change to something else, or if it does, - # that we create a different tracked function with that - # variable. - self.expr = lambda_element._invoke_user_fn(tracker_instrumented_fn) - - def _coerce_expression(self, lambda_element, apply_propagate_attrs): - """Run the tracker-generated expression through coercion rules. - - After the user-defined lambda has been invoked to produce a statement - for re-use, run it through coercion rules to both check that it's the - correct type of object and also to coerce it to its useful form. - - """ - - parent_lambda = lambda_element.parent_lambda - expr = self.expr - - if parent_lambda is None: - if isinstance(expr, collections_abc.Sequence): - self.expected_expr = [ - cast( - "ClauseElement", - coercions.expect( - lambda_element.role, - sub_expr, - apply_propagate_attrs=apply_propagate_attrs, - ), - ) - for sub_expr in expr - ] - self.is_sequence = True - else: - self.expected_expr = cast( - "ClauseElement", - coercions.expect( - lambda_element.role, - expr, - apply_propagate_attrs=apply_propagate_attrs, - ), - ) - self.is_sequence = False - else: - self.expected_expr = expr - self.is_sequence = False - - if apply_propagate_attrs is not None: - self.propagate_attrs = apply_propagate_attrs._propagate_attrs - else: - self.propagate_attrs = util.EMPTY_DICT - - def _rewrite_code_obj(self, f, cell_values, globals_): - """Return a copy of f, with a new closure and new globals - - yes it works in pypy :P - - """ - - argrange = range(len(cell_values)) - - code = "def make_cells():\n" - if cell_values: - code += " (%s) = (%s)\n" % ( - ", ".join("i%d" % i for i in argrange), - ", ".join("o%d" % i for i in argrange), - ) - code += " def closure():\n" - code += " return %s\n" % ", ".join("i%d" % i for i in argrange) - code += " return closure.__closure__" - vars_ = {"o%d" % i: cell_values[i] for i in argrange} - exec(code, vars_, vars_) - closure = vars_["make_cells"]() - - func = type(f)( - f.__code__, globals_, f.__name__, f.__defaults__, closure - ) - func.__annotations__ = f.__annotations__ - func.__kwdefaults__ = f.__kwdefaults__ - func.__doc__ = f.__doc__ - func.__module__ = f.__module__ - - return func - - -class PyWrapper(ColumnOperators): - """A wrapper object that is injected into the ``__globals__`` and - ``__closure__`` of a Python function. - - When the function is instrumented with :class:`.PyWrapper` objects, it is - then invoked just once in order to set up the wrappers. We look through - all the :class:`.PyWrapper` objects we made to find the ones that generated - a :class:`.BindParameter` object, e.g. the expression system interpreted - something as a literal. Those positions in the globals/closure are then - ones that we will look at, each time a new lambda comes in that refers to - the same ``__code__`` object. In this way, we keep a single version of - the SQL expression that this lambda produced, without calling upon the - Python function that created it more than once, unless its other closure - variables have changed. The expression is then transformed to have the - new bound values embedded into it. - - """ - - def __init__( - self, - fn, - name, - to_evaluate, - closure_index=None, - getter=None, - track_bound_values=True, - ): - self.fn = fn - self._name = name - self._to_evaluate = to_evaluate - self._param = None - self._has_param = False - self._bind_paths = {} - self._getter = getter - self._closure_index = closure_index - self.track_bound_values = track_bound_values - - def __call__(self, *arg, **kw): - elem = object.__getattribute__(self, "_to_evaluate") - value = elem(*arg, **kw) - if ( - self._sa_track_bound_values - and coercions._deep_is_literal(value) - and not isinstance( - # TODO: coverage where an ORM option or similar is here - value, - _cache_key.HasCacheKey, - ) - ): - name = object.__getattribute__(self, "_name") - raise exc.InvalidRequestError( - "Can't invoke Python callable %s() inside of lambda " - "expression argument at %s; lambda SQL constructs should " - "not invoke functions from closure variables to produce " - "literal values since the " - "lambda SQL system normally extracts bound values without " - "actually " - "invoking the lambda or any functions within it. Call the " - "function outside of the " - "lambda and assign to a local variable that is used in the " - "lambda as a closure variable, or set " - "track_bound_values=False if the return value of this " - "function is used in some other way other than a SQL bound " - "value." % (name, self._sa_fn.__code__) - ) - else: - return value - - def operate(self, op, *other, **kwargs): - elem = object.__getattribute__(self, "_py_wrapper_literal")() - return op(elem, *other, **kwargs) - - def reverse_operate(self, op, other, **kwargs): - elem = object.__getattribute__(self, "_py_wrapper_literal")() - return op(other, elem, **kwargs) - - def _extract_bound_parameters(self, starting_point, result_list): - param = object.__getattribute__(self, "_param") - if param is not None: - param = param._with_value(starting_point, maintain_key=True) - result_list.append(param) - for pywrapper in object.__getattribute__(self, "_bind_paths").values(): - getter = object.__getattribute__(pywrapper, "_getter") - element = getter(starting_point) - pywrapper._sa__extract_bound_parameters(element, result_list) - - def _py_wrapper_literal(self, expr=None, operator=None, **kw): - param = object.__getattribute__(self, "_param") - to_evaluate = object.__getattribute__(self, "_to_evaluate") - if param is None: - name = object.__getattribute__(self, "_name") - self._param = param = elements.BindParameter( - name, - required=False, - unique=True, - _compared_to_operator=operator, - _compared_to_type=expr.type if expr is not None else None, - ) - self._has_param = True - return param._with_value(to_evaluate, maintain_key=True) - - def __bool__(self): - to_evaluate = object.__getattribute__(self, "_to_evaluate") - return bool(to_evaluate) - - def __getattribute__(self, key): - if key.startswith("_sa_"): - return object.__getattribute__(self, key[4:]) - elif key in ( - "__clause_element__", - "operate", - "reverse_operate", - "_py_wrapper_literal", - "__class__", - "__dict__", - ): - return object.__getattribute__(self, key) - - if key.startswith("__"): - elem = object.__getattribute__(self, "_to_evaluate") - return getattr(elem, key) - else: - return self._sa__add_getter(key, operator.attrgetter) - - def __iter__(self): - elem = object.__getattribute__(self, "_to_evaluate") - return iter(elem) - - def __getitem__(self, key): - elem = object.__getattribute__(self, "_to_evaluate") - if not hasattr(elem, "__getitem__"): - raise AttributeError("__getitem__") - - if isinstance(key, PyWrapper): - # TODO: coverage - raise exc.InvalidRequestError( - "Dictionary keys / list indexes inside of a cached " - "lambda must be Python literals only" - ) - return self._sa__add_getter(key, operator.itemgetter) - - def _add_getter(self, key, getter_fn): - bind_paths = object.__getattribute__(self, "_bind_paths") - - bind_path_key = (key, getter_fn) - if bind_path_key in bind_paths: - return bind_paths[bind_path_key] - - getter = getter_fn(key) - elem = object.__getattribute__(self, "_to_evaluate") - value = getter(elem) - - rolled_down_value = AnalyzedCode._roll_down_to_literal(value) - - if coercions._deep_is_literal(rolled_down_value): - wrapper = PyWrapper(self._sa_fn, key, value, getter=getter) - bind_paths[bind_path_key] = wrapper - return wrapper - else: - return value - - -@inspection._inspects(LambdaElement) -def insp(lmb): - return inspection.inspect(lmb._resolved) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/naming.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/naming.py deleted file mode 100644 index 7213ddb..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/naming.py +++ /dev/null @@ -1,212 +0,0 @@ -# sql/naming.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# 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 diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/operators.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/operators.py deleted file mode 100644 index 9fb096e..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/operators.py +++ /dev/null @@ -1,2573 +0,0 @@ -# sql/operators.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""Defines operators used in SQL expressions.""" - -from __future__ import annotations - -from enum import IntEnum -from operator import add as _uncast_add -from operator import and_ as _uncast_and_ -from operator import contains as _uncast_contains -from operator import eq as _uncast_eq -from operator import floordiv as _uncast_floordiv -from operator import ge as _uncast_ge -from operator import getitem as _uncast_getitem -from operator import gt as _uncast_gt -from operator import inv as _uncast_inv -from operator import le as _uncast_le -from operator import lshift as _uncast_lshift -from operator import lt as _uncast_lt -from operator import mod as _uncast_mod -from operator import mul as _uncast_mul -from operator import ne as _uncast_ne -from operator import neg as _uncast_neg -from operator import or_ as _uncast_or_ -from operator import rshift as _uncast_rshift -from operator import sub as _uncast_sub -from operator import truediv as _uncast_truediv -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Generic -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 exc -from .. import util -from ..util.typing import Literal -from ..util.typing import Protocol - -if typing.TYPE_CHECKING: - from ._typing import ColumnExpressionArgument - from .cache_key import CacheConst - from .elements import ColumnElement - from .type_api import TypeEngine - -_T = TypeVar("_T", bound=Any) -_FN = TypeVar("_FN", bound=Callable[..., Any]) - - -class OperatorType(Protocol): - """describe an op() function.""" - - __slots__ = () - - __name__: str - - @overload - def __call__( - self, - left: ColumnExpressionArgument[Any], - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> ColumnElement[Any]: ... - - @overload - def __call__( - self, - left: Operators, - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> Operators: ... - - def __call__( - self, - left: Any, - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> Operators: ... - - -add = cast(OperatorType, _uncast_add) -and_ = cast(OperatorType, _uncast_and_) -contains = cast(OperatorType, _uncast_contains) -eq = cast(OperatorType, _uncast_eq) -floordiv = cast(OperatorType, _uncast_floordiv) -ge = cast(OperatorType, _uncast_ge) -getitem = cast(OperatorType, _uncast_getitem) -gt = cast(OperatorType, _uncast_gt) -inv = cast(OperatorType, _uncast_inv) -le = cast(OperatorType, _uncast_le) -lshift = cast(OperatorType, _uncast_lshift) -lt = cast(OperatorType, _uncast_lt) -mod = cast(OperatorType, _uncast_mod) -mul = cast(OperatorType, _uncast_mul) -ne = cast(OperatorType, _uncast_ne) -neg = cast(OperatorType, _uncast_neg) -or_ = cast(OperatorType, _uncast_or_) -rshift = cast(OperatorType, _uncast_rshift) -sub = cast(OperatorType, _uncast_sub) -truediv = cast(OperatorType, _uncast_truediv) - - -class Operators: - """Base of comparison and logical operators. - - Implements base methods - :meth:`~sqlalchemy.sql.operators.Operators.operate` and - :meth:`~sqlalchemy.sql.operators.Operators.reverse_operate`, as well as - :meth:`~sqlalchemy.sql.operators.Operators.__and__`, - :meth:`~sqlalchemy.sql.operators.Operators.__or__`, - :meth:`~sqlalchemy.sql.operators.Operators.__invert__`. - - Usually is used via its most common subclass - :class:`.ColumnOperators`. - - """ - - __slots__ = () - - def __and__(self, other: Any) -> Operators: - """Implement the ``&`` operator. - - When used with SQL expressions, results in an - AND operation, equivalent to - :func:`_expression.and_`, that is:: - - a & b - - is equivalent to:: - - from sqlalchemy import and_ - and_(a, b) - - Care should be taken when using ``&`` regarding - operator precedence; the ``&`` operator has the highest precedence. - The operands should be enclosed in parenthesis if they contain - further sub expressions:: - - (a == 2) & (b == 4) - - """ - return self.operate(and_, other) - - def __or__(self, other: Any) -> Operators: - """Implement the ``|`` operator. - - When used with SQL expressions, results in an - OR operation, equivalent to - :func:`_expression.or_`, that is:: - - a | b - - is equivalent to:: - - from sqlalchemy import or_ - or_(a, b) - - Care should be taken when using ``|`` regarding - operator precedence; the ``|`` operator has the highest precedence. - The operands should be enclosed in parenthesis if they contain - further sub expressions:: - - (a == 2) | (b == 4) - - """ - return self.operate(or_, other) - - def __invert__(self) -> Operators: - """Implement the ``~`` operator. - - When used with SQL expressions, results in a - NOT operation, equivalent to - :func:`_expression.not_`, that is:: - - ~a - - is equivalent to:: - - from sqlalchemy import not_ - not_(a) - - """ - return self.operate(inv) - - def op( - self, - opstring: str, - precedence: int = 0, - is_comparison: bool = False, - return_type: Optional[ - Union[Type[TypeEngine[Any]], TypeEngine[Any]] - ] = None, - python_impl: Optional[Callable[..., Any]] = None, - ) -> Callable[[Any], Operators]: - """Produce a generic operator function. - - e.g.:: - - somecolumn.op("*")(5) - - produces:: - - somecolumn * 5 - - This function can also be used to make bitwise operators explicit. For - example:: - - somecolumn.op('&')(0xff) - - is a bitwise AND of the value in ``somecolumn``. - - :param opstring: a string which will be output as the infix operator - between this element and the expression passed to the - generated function. - - :param precedence: precedence which the database is expected to apply - to the operator in SQL expressions. This integer value acts as a hint - for the SQL compiler to know when explicit parenthesis should be - rendered around a particular operation. A lower number will cause the - expression to be parenthesized when applied against another operator - with higher precedence. The default value of ``0`` is lower than all - operators except for the comma (``,``) and ``AS`` operators. A value - of 100 will be higher or equal to all operators, and -100 will be - lower than or equal to all operators. - - .. seealso:: - - :ref:`faq_sql_expression_op_parenthesis` - detailed description - of how the SQLAlchemy SQL compiler renders parenthesis - - :param is_comparison: legacy; if True, the operator will be considered - as a "comparison" operator, that is which evaluates to a boolean - true/false value, like ``==``, ``>``, etc. This flag is provided - so that ORM relationships can establish that the operator is a - comparison operator when used in a custom join condition. - - Using the ``is_comparison`` parameter is superseded by using the - :meth:`.Operators.bool_op` method instead; this more succinct - operator sets this parameter automatically, but also provides - correct :pep:`484` typing support as the returned object will - express a "boolean" datatype, i.e. ``BinaryExpression[bool]``. - - :param return_type: a :class:`.TypeEngine` class or object that will - force the return type of an expression produced by this operator - to be of that type. By default, operators that specify - :paramref:`.Operators.op.is_comparison` will resolve to - :class:`.Boolean`, and those that do not will be of the same - type as the left-hand operand. - - :param python_impl: an optional Python function that can evaluate - two Python values in the same way as this operator works when - run on the database server. Useful for in-Python SQL expression - evaluation functions, such as for ORM hybrid attributes, and the - ORM "evaluator" used to match objects in a session after a multi-row - update or delete. - - e.g.:: - - >>> expr = column('x').op('+', python_impl=lambda a, b: a + b)('y') - - The operator for the above expression will also work for non-SQL - left and right objects:: - - >>> expr.operator(5, 10) - 15 - - .. versionadded:: 2.0 - - - .. seealso:: - - :meth:`.Operators.bool_op` - - :ref:`types_operators` - - :ref:`relationship_custom_operator` - - """ - operator = custom_op( - opstring, - precedence, - is_comparison, - return_type, - python_impl=python_impl, - ) - - def against(other: Any) -> Operators: - return operator(self, other) - - return against - - def bool_op( - self, - opstring: str, - precedence: int = 0, - python_impl: Optional[Callable[..., Any]] = None, - ) -> Callable[[Any], Operators]: - """Return a custom boolean operator. - - This method is shorthand for calling - :meth:`.Operators.op` and passing the - :paramref:`.Operators.op.is_comparison` - flag with True. A key advantage to using :meth:`.Operators.bool_op` - is that when using column constructs, the "boolean" nature of the - returned expression will be present for :pep:`484` purposes. - - .. seealso:: - - :meth:`.Operators.op` - - """ - return self.op( - opstring, - precedence=precedence, - is_comparison=True, - python_impl=python_impl, - ) - - def operate( - self, op: OperatorType, *other: Any, **kwargs: Any - ) -> Operators: - r"""Operate on an argument. - - This is the lowest level of operation, raises - :class:`NotImplementedError` by default. - - Overriding this on a subclass can allow common - behavior to be applied to all operations. - For example, overriding :class:`.ColumnOperators` - to apply ``func.lower()`` to the left and right - side:: - - class MyComparator(ColumnOperators): - def operate(self, op, other, **kwargs): - return op(func.lower(self), func.lower(other), **kwargs) - - :param op: Operator callable. - :param \*other: the 'other' side of the operation. Will - be a single scalar for most operations. - :param \**kwargs: modifiers. These may be passed by special - operators such as :meth:`ColumnOperators.contains`. - - - """ - raise NotImplementedError(str(op)) - - __sa_operate__ = operate - - def reverse_operate( - self, op: OperatorType, other: Any, **kwargs: Any - ) -> Operators: - """Reverse operate on an argument. - - Usage is the same as :meth:`operate`. - - """ - raise NotImplementedError(str(op)) - - -class custom_op(OperatorType, Generic[_T]): - """Represent a 'custom' operator. - - :class:`.custom_op` is normally instantiated when the - :meth:`.Operators.op` or :meth:`.Operators.bool_op` methods - are used to create a custom operator callable. The class can also be - used directly when programmatically constructing expressions. E.g. - to represent the "factorial" operation:: - - from sqlalchemy.sql import UnaryExpression - from sqlalchemy.sql import operators - from sqlalchemy import Numeric - - unary = UnaryExpression(table.c.somecolumn, - modifier=operators.custom_op("!"), - type_=Numeric) - - - .. seealso:: - - :meth:`.Operators.op` - - :meth:`.Operators.bool_op` - - """ - - __name__ = "custom_op" - - __slots__ = ( - "opstring", - "precedence", - "is_comparison", - "natural_self_precedent", - "eager_grouping", - "return_type", - "python_impl", - ) - - def __init__( - self, - opstring: str, - precedence: int = 0, - is_comparison: bool = False, - return_type: Optional[ - Union[Type[TypeEngine[_T]], TypeEngine[_T]] - ] = None, - natural_self_precedent: bool = False, - eager_grouping: bool = False, - python_impl: Optional[Callable[..., Any]] = None, - ): - self.opstring = opstring - self.precedence = precedence - self.is_comparison = is_comparison - self.natural_self_precedent = natural_self_precedent - self.eager_grouping = eager_grouping - self.return_type = ( - return_type._to_instance(return_type) if return_type else None - ) - self.python_impl = python_impl - - def __eq__(self, other: Any) -> bool: - return ( - isinstance(other, custom_op) - and other._hash_key() == self._hash_key() - ) - - def __hash__(self) -> int: - return hash(self._hash_key()) - - def _hash_key(self) -> Union[CacheConst, Tuple[Any, ...]]: - return ( - self.__class__, - self.opstring, - self.precedence, - self.is_comparison, - self.natural_self_precedent, - self.eager_grouping, - self.return_type._static_cache_key if self.return_type else None, - ) - - @overload - def __call__( - self, - left: ColumnExpressionArgument[Any], - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> ColumnElement[Any]: ... - - @overload - def __call__( - self, - left: Operators, - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> Operators: ... - - def __call__( - self, - left: Any, - right: Optional[Any] = None, - *other: Any, - **kwargs: Any, - ) -> Operators: - if hasattr(left, "__sa_operate__"): - return left.operate(self, right, *other, **kwargs) # type: ignore - elif self.python_impl: - return self.python_impl(left, right, *other, **kwargs) # type: ignore # noqa: E501 - else: - raise exc.InvalidRequestError( - f"Custom operator {self.opstring!r} can't be used with " - "plain Python objects unless it includes the " - "'python_impl' parameter." - ) - - -class ColumnOperators(Operators): - """Defines boolean, comparison, and other operators for - :class:`_expression.ColumnElement` expressions. - - By default, all methods call down to - :meth:`.operate` or :meth:`.reverse_operate`, - passing in the appropriate operator function from the - Python builtin ``operator`` module or - a SQLAlchemy-specific operator function from - :mod:`sqlalchemy.expression.operators`. For example - the ``__eq__`` function:: - - def __eq__(self, other): - return self.operate(operators.eq, other) - - Where ``operators.eq`` is essentially:: - - def eq(a, b): - return a == b - - The core column expression unit :class:`_expression.ColumnElement` - overrides :meth:`.Operators.operate` and others - to return further :class:`_expression.ColumnElement` constructs, - so that the ``==`` operation above is replaced by a clause - construct. - - .. seealso:: - - :ref:`types_operators` - - :attr:`.TypeEngine.comparator_factory` - - :class:`.ColumnOperators` - - :class:`.PropComparator` - - """ - - __slots__ = () - - timetuple: Literal[None] = None - """Hack, allows datetime objects to be compared on the LHS.""" - - if typing.TYPE_CHECKING: - - def operate( - self, op: OperatorType, *other: Any, **kwargs: Any - ) -> ColumnOperators: ... - - def reverse_operate( - self, op: OperatorType, other: Any, **kwargs: Any - ) -> ColumnOperators: ... - - def __lt__(self, other: Any) -> ColumnOperators: - """Implement the ``<`` operator. - - In a column context, produces the clause ``a < b``. - - """ - return self.operate(lt, other) - - def __le__(self, other: Any) -> ColumnOperators: - """Implement the ``<=`` operator. - - In a column context, produces the clause ``a <= b``. - - """ - return self.operate(le, other) - - # ColumnOperators defines an __eq__ so it must explicitly declare also - # an hash or it's set to None by python: - # https://docs.python.org/3/reference/datamodel.html#object.__hash__ - if TYPE_CHECKING: - - def __hash__(self) -> int: ... - - else: - __hash__ = Operators.__hash__ - - def __eq__(self, other: Any) -> ColumnOperators: # type: ignore[override] - """Implement the ``==`` operator. - - In a column context, produces the clause ``a = b``. - If the target is ``None``, produces ``a IS NULL``. - - """ - return self.operate(eq, other) - - def __ne__(self, other: Any) -> ColumnOperators: # type: ignore[override] - """Implement the ``!=`` operator. - - In a column context, produces the clause ``a != b``. - If the target is ``None``, produces ``a IS NOT NULL``. - - """ - return self.operate(ne, other) - - def is_distinct_from(self, other: Any) -> ColumnOperators: - """Implement the ``IS DISTINCT FROM`` operator. - - Renders "a IS DISTINCT FROM b" on most platforms; - on some such as SQLite may render "a IS NOT b". - - """ - return self.operate(is_distinct_from, other) - - def is_not_distinct_from(self, other: Any) -> ColumnOperators: - """Implement the ``IS NOT DISTINCT FROM`` operator. - - Renders "a IS NOT DISTINCT FROM b" on most platforms; - on some such as SQLite may render "a IS b". - - .. versionchanged:: 1.4 The ``is_not_distinct_from()`` operator is - renamed from ``isnot_distinct_from()`` in previous releases. - The previous name remains available for backwards compatibility. - - """ - return self.operate(is_not_distinct_from, other) - - # deprecated 1.4; see #5435 - if TYPE_CHECKING: - - def isnot_distinct_from(self, other: Any) -> ColumnOperators: ... - - else: - isnot_distinct_from = is_not_distinct_from - - def __gt__(self, other: Any) -> ColumnOperators: - """Implement the ``>`` operator. - - In a column context, produces the clause ``a > b``. - - """ - return self.operate(gt, other) - - def __ge__(self, other: Any) -> ColumnOperators: - """Implement the ``>=`` operator. - - In a column context, produces the clause ``a >= b``. - - """ - return self.operate(ge, other) - - def __neg__(self) -> ColumnOperators: - """Implement the ``-`` operator. - - In a column context, produces the clause ``-a``. - - """ - return self.operate(neg) - - def __contains__(self, other: Any) -> ColumnOperators: - return self.operate(contains, other) - - def __getitem__(self, index: Any) -> ColumnOperators: - """Implement the [] operator. - - This can be used by some database-specific types - such as PostgreSQL ARRAY and HSTORE. - - """ - return self.operate(getitem, index) - - def __lshift__(self, other: Any) -> ColumnOperators: - """implement the << operator. - - Not used by SQLAlchemy core, this is provided - for custom operator systems which want to use - << as an extension point. - """ - return self.operate(lshift, other) - - def __rshift__(self, other: Any) -> ColumnOperators: - """implement the >> operator. - - Not used by SQLAlchemy core, this is provided - for custom operator systems which want to use - >> as an extension point. - """ - return self.operate(rshift, other) - - def concat(self, other: Any) -> ColumnOperators: - """Implement the 'concat' operator. - - In a column context, produces the clause ``a || b``, - or uses the ``concat()`` operator on MySQL. - - """ - return self.operate(concat_op, other) - - def _rconcat(self, other: Any) -> ColumnOperators: - """Implement an 'rconcat' operator. - - this is for internal use at the moment - - .. versionadded:: 1.4.40 - - """ - return self.reverse_operate(concat_op, other) - - def like( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: - r"""Implement the ``like`` operator. - - In a column context, produces the expression:: - - a LIKE other - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.like("%foobar%")) - - :param other: expression to be compared - :param escape: optional escape character, renders the ``ESCAPE`` - keyword, e.g.:: - - somecolumn.like("foo/%bar", escape="/") - - .. seealso:: - - :meth:`.ColumnOperators.ilike` - - """ - return self.operate(like_op, other, escape=escape) - - def ilike( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: - r"""Implement the ``ilike`` operator, e.g. case insensitive LIKE. - - In a column context, produces an expression either of the form:: - - lower(a) LIKE lower(other) - - Or on backends that support the ILIKE operator:: - - a ILIKE other - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.ilike("%foobar%")) - - :param other: expression to be compared - :param escape: optional escape character, renders the ``ESCAPE`` - keyword, e.g.:: - - somecolumn.ilike("foo/%bar", escape="/") - - .. seealso:: - - :meth:`.ColumnOperators.like` - - """ - return self.operate(ilike_op, other, escape=escape) - - def bitwise_xor(self, other: Any) -> ColumnOperators: - """Produce a bitwise XOR operation, typically via the ``^`` - operator, or ``#`` for PostgreSQL. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_xor_op, other) - - def bitwise_or(self, other: Any) -> ColumnOperators: - """Produce a bitwise OR operation, typically via the ``|`` - operator. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_or_op, other) - - def bitwise_and(self, other: Any) -> ColumnOperators: - """Produce a bitwise AND operation, typically via the ``&`` - operator. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_and_op, other) - - def bitwise_not(self) -> ColumnOperators: - """Produce a bitwise NOT operation, typically via the ``~`` - operator. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_not_op) - - def bitwise_lshift(self, other: Any) -> ColumnOperators: - """Produce a bitwise LSHIFT operation, typically via the ``<<`` - operator. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_lshift_op, other) - - def bitwise_rshift(self, other: Any) -> ColumnOperators: - """Produce a bitwise RSHIFT operation, typically via the ``>>`` - operator. - - .. versionadded:: 2.0.2 - - .. seealso:: - - :ref:`operators_bitwise` - - """ - - return self.operate(bitwise_rshift_op, other) - - def in_(self, other: Any) -> ColumnOperators: - """Implement the ``in`` operator. - - In a column context, produces the clause ``column IN <other>``. - - The given parameter ``other`` may be: - - * A list of literal values, e.g.:: - - stmt.where(column.in_([1, 2, 3])) - - In this calling form, the list of items is converted to a set of - bound parameters the same length as the list given:: - - WHERE COL IN (?, ?, ?) - - * A list of tuples may be provided if the comparison is against a - :func:`.tuple_` containing multiple expressions:: - - from sqlalchemy import tuple_ - stmt.where(tuple_(col1, col2).in_([(1, 10), (2, 20), (3, 30)])) - - * An empty list, e.g.:: - - stmt.where(column.in_([])) - - In this calling form, the expression renders an "empty set" - expression. These expressions are tailored to individual backends - and are generally trying to get an empty SELECT statement as a - subquery. Such as on SQLite, the expression is:: - - WHERE col IN (SELECT 1 FROM (SELECT 1) WHERE 1!=1) - - .. versionchanged:: 1.4 empty IN expressions now use an - execution-time generated SELECT subquery in all cases. - - * A bound parameter, e.g. :func:`.bindparam`, may be used if it - includes the :paramref:`.bindparam.expanding` flag:: - - stmt.where(column.in_(bindparam('value', expanding=True))) - - In this calling form, the expression renders a special non-SQL - placeholder expression that looks like:: - - WHERE COL IN ([EXPANDING_value]) - - This placeholder expression is intercepted at statement execution - time to be converted into the variable number of bound parameter - form illustrated earlier. If the statement were executed as:: - - connection.execute(stmt, {"value": [1, 2, 3]}) - - The database would be passed a bound parameter for each value:: - - WHERE COL IN (?, ?, ?) - - .. versionadded:: 1.2 added "expanding" bound parameters - - If an empty list is passed, a special "empty list" expression, - which is specific to the database in use, is rendered. On - SQLite this would be:: - - WHERE COL IN (SELECT 1 FROM (SELECT 1) WHERE 1!=1) - - .. versionadded:: 1.3 "expanding" bound parameters now support - empty lists - - * a :func:`_expression.select` construct, which is usually a - correlated scalar select:: - - stmt.where( - column.in_( - select(othertable.c.y). - where(table.c.x == othertable.c.x) - ) - ) - - In this calling form, :meth:`.ColumnOperators.in_` renders as given:: - - WHERE COL IN (SELECT othertable.y - FROM othertable WHERE othertable.x = table.x) - - :param other: a list of literals, a :func:`_expression.select` - construct, or a :func:`.bindparam` construct that includes the - :paramref:`.bindparam.expanding` flag set to True. - - """ - return self.operate(in_op, other) - - def not_in(self, other: Any) -> ColumnOperators: - """implement the ``NOT IN`` operator. - - This is equivalent to using negation with - :meth:`.ColumnOperators.in_`, i.e. ``~x.in_(y)``. - - In the case that ``other`` is an empty sequence, the compiler - produces an "empty not in" expression. This defaults to the - expression "1 = 1" to produce true in all cases. The - :paramref:`_sa.create_engine.empty_in_strategy` may be used to - alter this behavior. - - .. versionchanged:: 1.4 The ``not_in()`` operator is renamed from - ``notin_()`` in previous releases. The previous name remains - available for backwards compatibility. - - .. versionchanged:: 1.2 The :meth:`.ColumnOperators.in_` and - :meth:`.ColumnOperators.not_in` operators - now produce a "static" expression for an empty IN sequence - by default. - - .. seealso:: - - :meth:`.ColumnOperators.in_` - - """ - return self.operate(not_in_op, other) - - # deprecated 1.4; see #5429 - if TYPE_CHECKING: - - def notin_(self, other: Any) -> ColumnOperators: ... - - else: - notin_ = not_in - - def not_like( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: - """implement the ``NOT LIKE`` operator. - - This is equivalent to using negation with - :meth:`.ColumnOperators.like`, i.e. ``~x.like(y)``. - - .. versionchanged:: 1.4 The ``not_like()`` operator is renamed from - ``notlike()`` in previous releases. The previous name remains - available for backwards compatibility. - - .. seealso:: - - :meth:`.ColumnOperators.like` - - """ - return self.operate(not_like_op, other, escape=escape) - - # deprecated 1.4; see #5435 - if TYPE_CHECKING: - - def notlike( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: ... - - else: - notlike = not_like - - def not_ilike( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: - """implement the ``NOT ILIKE`` operator. - - This is equivalent to using negation with - :meth:`.ColumnOperators.ilike`, i.e. ``~x.ilike(y)``. - - .. versionchanged:: 1.4 The ``not_ilike()`` operator is renamed from - ``notilike()`` in previous releases. The previous name remains - available for backwards compatibility. - - .. seealso:: - - :meth:`.ColumnOperators.ilike` - - """ - return self.operate(not_ilike_op, other, escape=escape) - - # deprecated 1.4; see #5435 - if TYPE_CHECKING: - - def notilike( - self, other: Any, escape: Optional[str] = None - ) -> ColumnOperators: ... - - else: - notilike = not_ilike - - def is_(self, other: Any) -> ColumnOperators: - """Implement the ``IS`` operator. - - Normally, ``IS`` is generated automatically when comparing to a - value of ``None``, which resolves to ``NULL``. However, explicit - usage of ``IS`` may be desirable if comparing to boolean values - on certain platforms. - - .. seealso:: :meth:`.ColumnOperators.is_not` - - """ - return self.operate(is_, other) - - def is_not(self, other: Any) -> ColumnOperators: - """Implement the ``IS NOT`` operator. - - Normally, ``IS NOT`` is generated automatically when comparing to a - value of ``None``, which resolves to ``NULL``. However, explicit - usage of ``IS NOT`` may be desirable if comparing to boolean values - on certain platforms. - - .. versionchanged:: 1.4 The ``is_not()`` operator is renamed from - ``isnot()`` in previous releases. The previous name remains - available for backwards compatibility. - - .. seealso:: :meth:`.ColumnOperators.is_` - - """ - return self.operate(is_not, other) - - # deprecated 1.4; see #5429 - if TYPE_CHECKING: - - def isnot(self, other: Any) -> ColumnOperators: ... - - else: - isnot = is_not - - def startswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnOperators: - r"""Implement the ``startswith`` operator. - - Produces a LIKE expression that tests against a match for the start - of a string value:: - - column LIKE <other> || '%' - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.startswith("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.startswith.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.startswith.escape` parameter will establish - a given character as an escape character which can be of use when - the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.startswith.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.startswith("foo%bar", autoescape=True) - - Will render as:: - - somecolumn LIKE :param || '%' ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.startswith("foo/%bar", escape="^") - - Will render as:: - - somecolumn LIKE :param || '%' ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.startswith.autoescape`:: - - somecolumn.startswith("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.endswith` - - :meth:`.ColumnOperators.contains` - - :meth:`.ColumnOperators.like` - - """ - return self.operate( - startswith_op, other, escape=escape, autoescape=autoescape - ) - - def istartswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnOperators: - r"""Implement the ``istartswith`` operator, e.g. case insensitive - version of :meth:`.ColumnOperators.startswith`. - - Produces a LIKE expression that tests against an insensitive - match for the start of a string value:: - - lower(column) LIKE lower(<other>) || '%' - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.istartswith("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.istartswith.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.istartswith.escape` parameter will - establish a given character as an escape character which can be of - use when the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.istartswith.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.istartswith("foo%bar", autoescape=True) - - Will render as:: - - lower(somecolumn) LIKE lower(:param) || '%' ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.istartswith("foo/%bar", escape="^") - - Will render as:: - - lower(somecolumn) LIKE lower(:param) || '%' ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.istartswith.autoescape`:: - - somecolumn.istartswith("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.startswith` - """ - return self.operate( - istartswith_op, other, escape=escape, autoescape=autoescape - ) - - def endswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnOperators: - r"""Implement the 'endswith' operator. - - Produces a LIKE expression that tests against a match for the end - of a string value:: - - column LIKE '%' || <other> - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.endswith("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.endswith.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.endswith.escape` parameter will establish - a given character as an escape character which can be of use when - the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.endswith.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.endswith("foo%bar", autoescape=True) - - Will render as:: - - somecolumn LIKE '%' || :param ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.endswith("foo/%bar", escape="^") - - Will render as:: - - somecolumn LIKE '%' || :param ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.endswith.autoescape`:: - - somecolumn.endswith("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.startswith` - - :meth:`.ColumnOperators.contains` - - :meth:`.ColumnOperators.like` - - """ - return self.operate( - endswith_op, other, escape=escape, autoescape=autoescape - ) - - def iendswith( - self, - other: Any, - escape: Optional[str] = None, - autoescape: bool = False, - ) -> ColumnOperators: - r"""Implement the ``iendswith`` operator, e.g. case insensitive - version of :meth:`.ColumnOperators.endswith`. - - Produces a LIKE expression that tests against an insensitive match - for the end of a string value:: - - lower(column) LIKE '%' || lower(<other>) - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.iendswith("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.iendswith.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.iendswith.escape` parameter will establish - a given character as an escape character which can be of use when - the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.iendswith.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.iendswith("foo%bar", autoescape=True) - - Will render as:: - - lower(somecolumn) LIKE '%' || lower(:param) ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.iendswith("foo/%bar", escape="^") - - Will render as:: - - lower(somecolumn) LIKE '%' || lower(:param) ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.iendswith.autoescape`:: - - somecolumn.endswith("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.endswith` - """ - return self.operate( - iendswith_op, other, escape=escape, autoescape=autoescape - ) - - def contains(self, other: Any, **kw: Any) -> ColumnOperators: - r"""Implement the 'contains' operator. - - Produces a LIKE expression that tests against a match for the middle - of a string value:: - - column LIKE '%' || <other> || '%' - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.contains("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.contains.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.contains.escape` parameter will establish - a given character as an escape character which can be of use when - the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.contains.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.contains("foo%bar", autoescape=True) - - Will render as:: - - somecolumn LIKE '%' || :param || '%' ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.contains("foo/%bar", escape="^") - - Will render as:: - - somecolumn LIKE '%' || :param || '%' ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.contains.autoescape`:: - - somecolumn.contains("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.startswith` - - :meth:`.ColumnOperators.endswith` - - :meth:`.ColumnOperators.like` - - - """ - return self.operate(contains_op, other, **kw) - - def icontains(self, other: Any, **kw: Any) -> ColumnOperators: - r"""Implement the ``icontains`` operator, e.g. case insensitive - version of :meth:`.ColumnOperators.contains`. - - Produces a LIKE expression that tests against an insensitive match - for the middle of a string value:: - - lower(column) LIKE '%' || lower(<other>) || '%' - - E.g.:: - - stmt = select(sometable).\ - where(sometable.c.column.icontains("foobar")) - - Since the operator uses ``LIKE``, wildcard characters - ``"%"`` and ``"_"`` that are present inside the <other> expression - will behave like wildcards as well. For literal string - values, the :paramref:`.ColumnOperators.icontains.autoescape` flag - may be set to ``True`` to apply escaping to occurrences of these - characters within the string value so that they match as themselves - and not as wildcard characters. Alternatively, the - :paramref:`.ColumnOperators.icontains.escape` parameter will establish - a given character as an escape character which can be of use when - the target expression is not a literal string. - - :param other: expression to be compared. This is usually a plain - string value, but can also be an arbitrary SQL expression. LIKE - wildcard characters ``%`` and ``_`` are not escaped by default unless - the :paramref:`.ColumnOperators.icontains.autoescape` flag is - set to True. - - :param autoescape: boolean; when True, establishes an escape character - within the LIKE expression, then applies it to all occurrences of - ``"%"``, ``"_"`` and the escape character itself within the - comparison value, which is assumed to be a literal string and not a - SQL expression. - - An expression such as:: - - somecolumn.icontains("foo%bar", autoescape=True) - - Will render as:: - - lower(somecolumn) LIKE '%' || lower(:param) || '%' ESCAPE '/' - - With the value of ``:param`` as ``"foo/%bar"``. - - :param escape: a character which when given will render with the - ``ESCAPE`` keyword to establish that character as the escape - character. This character can then be placed preceding occurrences - of ``%`` and ``_`` to allow them to act as themselves and not - wildcard characters. - - An expression such as:: - - somecolumn.icontains("foo/%bar", escape="^") - - Will render as:: - - lower(somecolumn) LIKE '%' || lower(:param) || '%' ESCAPE '^' - - The parameter may also be combined with - :paramref:`.ColumnOperators.contains.autoescape`:: - - somecolumn.icontains("foo%bar^bat", escape="^", autoescape=True) - - Where above, the given literal parameter will be converted to - ``"foo^%bar^^bat"`` before being passed to the database. - - .. seealso:: - - :meth:`.ColumnOperators.contains` - - """ - return self.operate(icontains_op, other, **kw) - - def match(self, other: Any, **kwargs: Any) -> ColumnOperators: - """Implements a database-specific 'match' operator. - - :meth:`_sql.ColumnOperators.match` attempts to resolve to - a MATCH-like function or operator provided by the backend. - Examples include: - - * PostgreSQL - renders ``x @@ plainto_tsquery(y)`` - - .. versionchanged:: 2.0 ``plainto_tsquery()`` is used instead - of ``to_tsquery()`` for PostgreSQL now; for compatibility with - other forms, see :ref:`postgresql_match`. - - - * MySQL - renders ``MATCH (x) AGAINST (y IN BOOLEAN MODE)`` - - .. seealso:: - - :class:`_mysql.match` - MySQL specific construct with - additional features. - - * Oracle - renders ``CONTAINS(x, y)`` - * other backends may provide special implementations. - * Backends without any special implementation will emit - the operator as "MATCH". This is compatible with SQLite, for - example. - - """ - return self.operate(match_op, other, **kwargs) - - def regexp_match( - self, pattern: Any, flags: Optional[str] = None - ) -> ColumnOperators: - """Implements a database-specific 'regexp match' operator. - - E.g.:: - - stmt = select(table.c.some_column).where( - table.c.some_column.regexp_match('^(b|c)') - ) - - :meth:`_sql.ColumnOperators.regexp_match` attempts to resolve to - a REGEXP-like function or operator provided by the backend, however - the specific regular expression syntax and flags available are - **not backend agnostic**. - - Examples include: - - * PostgreSQL - renders ``x ~ y`` or ``x !~ y`` when negated. - * Oracle - renders ``REGEXP_LIKE(x, y)`` - * SQLite - uses SQLite's ``REGEXP`` placeholder operator and calls into - the Python ``re.match()`` builtin. - * other backends may provide special implementations. - * Backends without any special implementation will emit - the operator as "REGEXP" or "NOT REGEXP". This is compatible with - SQLite and MySQL, for example. - - Regular expression support is currently implemented for Oracle, - PostgreSQL, MySQL and MariaDB. Partial support is available for - SQLite. Support among third-party dialects may vary. - - :param pattern: The regular expression pattern string or column - clause. - :param flags: Any regular expression string flags to apply, passed as - plain Python string only. These flags are backend specific. - Some backends, like PostgreSQL and MariaDB, may alternatively - specify the flags as part of the pattern. - When using the ignore case flag 'i' in PostgreSQL, the ignore case - regexp match operator ``~*`` or ``!~*`` will be used. - - .. versionadded:: 1.4 - - .. versionchanged:: 1.4.48, 2.0.18 Note that due to an implementation - error, the "flags" parameter previously accepted SQL expression - objects such as column expressions in addition to plain Python - strings. This implementation did not work correctly with caching - and was removed; strings only should be passed for the "flags" - parameter, as these flags are rendered as literal inline values - within SQL expressions. - - .. seealso:: - - :meth:`_sql.ColumnOperators.regexp_replace` - - - """ - return self.operate(regexp_match_op, pattern, flags=flags) - - def regexp_replace( - self, pattern: Any, replacement: Any, flags: Optional[str] = None - ) -> ColumnOperators: - """Implements a database-specific 'regexp replace' operator. - - E.g.:: - - stmt = select( - table.c.some_column.regexp_replace( - 'b(..)', - 'X\1Y', - flags='g' - ) - ) - - :meth:`_sql.ColumnOperators.regexp_replace` attempts to resolve to - a REGEXP_REPLACE-like function provided by the backend, that - usually emit the function ``REGEXP_REPLACE()``. However, - the specific regular expression syntax and flags available are - **not backend agnostic**. - - Regular expression replacement support is currently implemented for - Oracle, PostgreSQL, MySQL 8 or greater and MariaDB. Support among - third-party dialects may vary. - - :param pattern: The regular expression pattern string or column - clause. - :param pattern: The replacement string or column clause. - :param flags: Any regular expression string flags to apply, passed as - plain Python string only. These flags are backend specific. - Some backends, like PostgreSQL and MariaDB, may alternatively - specify the flags as part of the pattern. - - .. versionadded:: 1.4 - - .. versionchanged:: 1.4.48, 2.0.18 Note that due to an implementation - error, the "flags" parameter previously accepted SQL expression - objects such as column expressions in addition to plain Python - strings. This implementation did not work correctly with caching - and was removed; strings only should be passed for the "flags" - parameter, as these flags are rendered as literal inline values - within SQL expressions. - - - .. seealso:: - - :meth:`_sql.ColumnOperators.regexp_match` - - """ - return self.operate( - regexp_replace_op, - pattern, - replacement=replacement, - flags=flags, - ) - - def desc(self) -> ColumnOperators: - """Produce a :func:`_expression.desc` clause against the - parent object.""" - return self.operate(desc_op) - - def asc(self) -> ColumnOperators: - """Produce a :func:`_expression.asc` clause against the - parent object.""" - return self.operate(asc_op) - - def nulls_first(self) -> ColumnOperators: - """Produce a :func:`_expression.nulls_first` clause against the - parent object. - - .. versionchanged:: 1.4 The ``nulls_first()`` operator is - renamed from ``nullsfirst()`` in previous releases. - The previous name remains available for backwards compatibility. - """ - return self.operate(nulls_first_op) - - # deprecated 1.4; see #5435 - if TYPE_CHECKING: - - def nullsfirst(self) -> ColumnOperators: ... - - else: - nullsfirst = nulls_first - - def nulls_last(self) -> ColumnOperators: - """Produce a :func:`_expression.nulls_last` clause against the - parent object. - - .. versionchanged:: 1.4 The ``nulls_last()`` operator is - renamed from ``nullslast()`` in previous releases. - The previous name remains available for backwards compatibility. - """ - return self.operate(nulls_last_op) - - # deprecated 1.4; see #5429 - if TYPE_CHECKING: - - def nullslast(self) -> ColumnOperators: ... - - else: - nullslast = nulls_last - - def collate(self, collation: str) -> ColumnOperators: - """Produce a :func:`_expression.collate` clause against - the parent object, given the collation string. - - .. seealso:: - - :func:`_expression.collate` - - """ - return self.operate(collate, collation) - - def __radd__(self, other: Any) -> ColumnOperators: - """Implement the ``+`` operator in reverse. - - See :meth:`.ColumnOperators.__add__`. - - """ - return self.reverse_operate(add, other) - - def __rsub__(self, other: Any) -> ColumnOperators: - """Implement the ``-`` operator in reverse. - - See :meth:`.ColumnOperators.__sub__`. - - """ - return self.reverse_operate(sub, other) - - def __rmul__(self, other: Any) -> ColumnOperators: - """Implement the ``*`` operator in reverse. - - See :meth:`.ColumnOperators.__mul__`. - - """ - return self.reverse_operate(mul, other) - - def __rmod__(self, other: Any) -> ColumnOperators: - """Implement the ``%`` operator in reverse. - - See :meth:`.ColumnOperators.__mod__`. - - """ - return self.reverse_operate(mod, other) - - def between( - self, cleft: Any, cright: Any, symmetric: bool = False - ) -> ColumnOperators: - """Produce a :func:`_expression.between` clause against - the parent object, given the lower and upper range. - - """ - return self.operate(between_op, cleft, cright, symmetric=symmetric) - - def distinct(self) -> ColumnOperators: - """Produce a :func:`_expression.distinct` clause against the - parent object. - - """ - return self.operate(distinct_op) - - def any_(self) -> ColumnOperators: - """Produce an :func:`_expression.any_` clause against the - parent object. - - See the documentation for :func:`_sql.any_` for examples. - - .. note:: be sure to not confuse the newer - :meth:`_sql.ColumnOperators.any_` method with the **legacy** - version of this method, the :meth:`_types.ARRAY.Comparator.any` - method that's specific to :class:`_types.ARRAY`, which uses a - different calling style. - - """ - return self.operate(any_op) - - def all_(self) -> ColumnOperators: - """Produce an :func:`_expression.all_` clause against the - parent object. - - See the documentation for :func:`_sql.all_` for examples. - - .. note:: be sure to not confuse the newer - :meth:`_sql.ColumnOperators.all_` method with the **legacy** - version of this method, the :meth:`_types.ARRAY.Comparator.all` - method that's specific to :class:`_types.ARRAY`, which uses a - different calling style. - - """ - return self.operate(all_op) - - def __add__(self, other: Any) -> ColumnOperators: - """Implement the ``+`` operator. - - In a column context, produces the clause ``a + b`` - if the parent object has non-string affinity. - If the parent object has a string affinity, - produces the concatenation operator, ``a || b`` - - see :meth:`.ColumnOperators.concat`. - - """ - return self.operate(add, other) - - def __sub__(self, other: Any) -> ColumnOperators: - """Implement the ``-`` operator. - - In a column context, produces the clause ``a - b``. - - """ - return self.operate(sub, other) - - def __mul__(self, other: Any) -> ColumnOperators: - """Implement the ``*`` operator. - - In a column context, produces the clause ``a * b``. - - """ - return self.operate(mul, other) - - def __mod__(self, other: Any) -> ColumnOperators: - """Implement the ``%`` operator. - - In a column context, produces the clause ``a % b``. - - """ - return self.operate(mod, other) - - def __truediv__(self, other: Any) -> ColumnOperators: - """Implement the ``/`` operator. - - In a column context, produces the clause ``a / b``, and - considers the result type to be numeric. - - .. versionchanged:: 2.0 The truediv operator against two integers - is now considered to return a numeric value. Behavior on specific - backends may vary. - - """ - return self.operate(truediv, other) - - def __rtruediv__(self, other: Any) -> ColumnOperators: - """Implement the ``/`` operator in reverse. - - See :meth:`.ColumnOperators.__truediv__`. - - """ - return self.reverse_operate(truediv, other) - - def __floordiv__(self, other: Any) -> ColumnOperators: - """Implement the ``//`` operator. - - In a column context, produces the clause ``a / b``, - which is the same as "truediv", but considers the result - type to be integer. - - .. versionadded:: 2.0 - - """ - return self.operate(floordiv, other) - - def __rfloordiv__(self, other: Any) -> ColumnOperators: - """Implement the ``//`` operator in reverse. - - See :meth:`.ColumnOperators.__floordiv__`. - - """ - return self.reverse_operate(floordiv, other) - - -_commutative: Set[Any] = {eq, ne, add, mul} -_comparison: Set[Any] = {eq, ne, lt, gt, ge, le} - - -def _operator_fn(fn: Callable[..., Any]) -> OperatorType: - return cast(OperatorType, fn) - - -def commutative_op(fn: _FN) -> _FN: - _commutative.add(fn) - return fn - - -def comparison_op(fn: _FN) -> _FN: - _comparison.add(fn) - return fn - - -@_operator_fn -def from_() -> Any: - raise NotImplementedError() - - -@_operator_fn -@comparison_op -def function_as_comparison_op() -> Any: - raise NotImplementedError() - - -@_operator_fn -def as_() -> Any: - raise NotImplementedError() - - -@_operator_fn -def exists() -> Any: - raise NotImplementedError() - - -@_operator_fn -def is_true(a: Any) -> Any: - raise NotImplementedError() - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def istrue(a: Any) -> Any: ... - -else: - istrue = is_true - - -@_operator_fn -def is_false(a: Any) -> Any: - raise NotImplementedError() - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def isfalse(a: Any) -> Any: ... - -else: - isfalse = is_false - - -@comparison_op -@_operator_fn -def is_distinct_from(a: Any, b: Any) -> Any: - return a.is_distinct_from(b) - - -@comparison_op -@_operator_fn -def is_not_distinct_from(a: Any, b: Any) -> Any: - return a.is_not_distinct_from(b) - - -# deprecated 1.4; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def isnot_distinct_from(a: Any, b: Any) -> Any: ... - -else: - isnot_distinct_from = is_not_distinct_from - - -@comparison_op -@_operator_fn -def is_(a: Any, b: Any) -> Any: - return a.is_(b) - - -@comparison_op -@_operator_fn -def is_not(a: Any, b: Any) -> Any: - return a.is_not(b) - - -# 1.4 deprecated; see #5429 -if TYPE_CHECKING: - - @_operator_fn - def isnot(a: Any, b: Any) -> Any: ... - -else: - isnot = is_not - - -@_operator_fn -def collate(a: Any, b: Any) -> Any: - return a.collate(b) - - -@_operator_fn -def op(a: Any, opstring: str, b: Any) -> Any: - return a.op(opstring)(b) - - -@comparison_op -@_operator_fn -def like_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: - return a.like(b, escape=escape) - - -@comparison_op -@_operator_fn -def not_like_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: - return a.notlike(b, escape=escape) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notlike_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: ... - -else: - notlike_op = not_like_op - - -@comparison_op -@_operator_fn -def ilike_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: - return a.ilike(b, escape=escape) - - -@comparison_op -@_operator_fn -def not_ilike_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: - return a.not_ilike(b, escape=escape) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notilike_op(a: Any, b: Any, escape: Optional[str] = None) -> Any: ... - -else: - notilike_op = not_ilike_op - - -@comparison_op -@_operator_fn -def between_op(a: Any, b: Any, c: Any, symmetric: bool = False) -> Any: - return a.between(b, c, symmetric=symmetric) - - -@comparison_op -@_operator_fn -def not_between_op(a: Any, b: Any, c: Any, symmetric: bool = False) -> Any: - return ~a.between(b, c, symmetric=symmetric) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notbetween_op( - a: Any, b: Any, c: Any, symmetric: bool = False - ) -> Any: ... - -else: - notbetween_op = not_between_op - - -@comparison_op -@_operator_fn -def in_op(a: Any, b: Any) -> Any: - return a.in_(b) - - -@comparison_op -@_operator_fn -def not_in_op(a: Any, b: Any) -> Any: - return a.not_in(b) - - -# 1.4 deprecated; see #5429 -if TYPE_CHECKING: - - @_operator_fn - def notin_op(a: Any, b: Any) -> Any: ... - -else: - notin_op = not_in_op - - -@_operator_fn -def distinct_op(a: Any) -> Any: - return a.distinct() - - -@_operator_fn -def any_op(a: Any) -> Any: - return a.any_() - - -@_operator_fn -def all_op(a: Any) -> Any: - return a.all_() - - -def _escaped_like_impl( - fn: Callable[..., Any], other: Any, escape: Optional[str], autoescape: bool -) -> Any: - if autoescape: - if autoescape is not True: - util.warn( - "The autoescape parameter is now a simple boolean True/False" - ) - if escape is None: - escape = "/" - - if not isinstance(other, str): - raise TypeError("String value expected when autoescape=True") - - if escape not in ("%", "_"): - other = other.replace(escape, escape + escape) - - other = other.replace("%", escape + "%").replace("_", escape + "_") - - return fn(other, escape=escape) - - -@comparison_op -@_operator_fn -def startswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.startswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_startswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.startswith, b, escape, autoescape) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notstartswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False - ) -> Any: ... - -else: - notstartswith_op = not_startswith_op - - -@comparison_op -@_operator_fn -def istartswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.istartswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_istartswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.istartswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def endswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.endswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_endswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.endswith, b, escape, autoescape) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notendswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False - ) -> Any: ... - -else: - notendswith_op = not_endswith_op - - -@comparison_op -@_operator_fn -def iendswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.iendswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_iendswith_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.iendswith, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def contains_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.contains, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_contains_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.contains, b, escape, autoescape) - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def notcontains_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False - ) -> Any: ... - -else: - notcontains_op = not_contains_op - - -@comparison_op -@_operator_fn -def icontains_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return _escaped_like_impl(a.icontains, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def not_icontains_op( - a: Any, b: Any, escape: Optional[str] = None, autoescape: bool = False -) -> Any: - return ~_escaped_like_impl(a.icontains, b, escape, autoescape) - - -@comparison_op -@_operator_fn -def match_op(a: Any, b: Any, **kw: Any) -> Any: - return a.match(b, **kw) - - -@comparison_op -@_operator_fn -def regexp_match_op(a: Any, b: Any, flags: Optional[str] = None) -> Any: - return a.regexp_match(b, flags=flags) - - -@comparison_op -@_operator_fn -def not_regexp_match_op(a: Any, b: Any, flags: Optional[str] = None) -> Any: - return ~a.regexp_match(b, flags=flags) - - -@_operator_fn -def regexp_replace_op( - a: Any, b: Any, replacement: Any, flags: Optional[str] = None -) -> Any: - return a.regexp_replace(b, replacement=replacement, flags=flags) - - -@comparison_op -@_operator_fn -def not_match_op(a: Any, b: Any, **kw: Any) -> Any: - return ~a.match(b, **kw) - - -# 1.4 deprecated; see #5429 -if TYPE_CHECKING: - - @_operator_fn - def notmatch_op(a: Any, b: Any, **kw: Any) -> Any: ... - -else: - notmatch_op = not_match_op - - -@_operator_fn -def comma_op(a: Any, b: Any) -> Any: - raise NotImplementedError() - - -@_operator_fn -def filter_op(a: Any, b: Any) -> Any: - raise NotImplementedError() - - -@_operator_fn -def concat_op(a: Any, b: Any) -> Any: - try: - concat = a.concat - except AttributeError: - return b._rconcat(a) - else: - return concat(b) - - -@_operator_fn -def desc_op(a: Any) -> Any: - return a.desc() - - -@_operator_fn -def asc_op(a: Any) -> Any: - return a.asc() - - -@_operator_fn -def nulls_first_op(a: Any) -> Any: - return a.nulls_first() - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def nullsfirst_op(a: Any) -> Any: ... - -else: - nullsfirst_op = nulls_first_op - - -@_operator_fn -def nulls_last_op(a: Any) -> Any: - return a.nulls_last() - - -# 1.4 deprecated; see #5435 -if TYPE_CHECKING: - - @_operator_fn - def nullslast_op(a: Any) -> Any: ... - -else: - nullslast_op = nulls_last_op - - -@_operator_fn -def json_getitem_op(a: Any, b: Any) -> Any: - raise NotImplementedError() - - -@_operator_fn -def json_path_getitem_op(a: Any, b: Any) -> Any: - raise NotImplementedError() - - -@_operator_fn -def bitwise_xor_op(a: Any, b: Any) -> Any: - return a.bitwise_xor(b) - - -@_operator_fn -def bitwise_or_op(a: Any, b: Any) -> Any: - return a.bitwise_or(b) - - -@_operator_fn -def bitwise_and_op(a: Any, b: Any) -> Any: - return a.bitwise_and(b) - - -@_operator_fn -def bitwise_not_op(a: Any) -> Any: - return a.bitwise_not() - - -@_operator_fn -def bitwise_lshift_op(a: Any, b: Any) -> Any: - return a.bitwise_lshift(b) - - -@_operator_fn -def bitwise_rshift_op(a: Any, b: Any) -> Any: - return a.bitwise_rshift(b) - - -def is_comparison(op: OperatorType) -> bool: - return op in _comparison or isinstance(op, custom_op) and op.is_comparison - - -def is_commutative(op: OperatorType) -> bool: - return op in _commutative - - -def is_ordering_modifier(op: OperatorType) -> bool: - return op in (asc_op, desc_op, nulls_first_op, nulls_last_op) - - -def is_natural_self_precedent(op: OperatorType) -> bool: - return ( - op in _natural_self_precedent - or isinstance(op, custom_op) - and op.natural_self_precedent - ) - - -_booleans = (inv, is_true, is_false, and_, or_) - - -def is_boolean(op: OperatorType) -> bool: - return is_comparison(op) or op in _booleans - - -_mirror = {gt: lt, ge: le, lt: gt, le: ge} - - -def mirror(op: OperatorType) -> OperatorType: - """rotate a comparison operator 180 degrees. - - Note this is not the same as negation. - - """ - return _mirror.get(op, op) - - -_associative = _commutative.union([concat_op, and_, or_]).difference([eq, ne]) - - -def is_associative(op: OperatorType) -> bool: - return op in _associative - - -_natural_self_precedent = _associative.union( - [getitem, json_getitem_op, json_path_getitem_op] -) -"""Operators where if we have (a op b) op c, we don't want to -parenthesize (a op b). - -""" - - -@_operator_fn -def _asbool(a: Any) -> Any: - raise NotImplementedError() - - -class _OpLimit(IntEnum): - _smallest = -100 - _largest = 100 - - -_PRECEDENCE: Dict[OperatorType, int] = { - from_: 15, - function_as_comparison_op: 15, - any_op: 15, - all_op: 15, - getitem: 15, - json_getitem_op: 15, - json_path_getitem_op: 15, - mul: 8, - truediv: 8, - floordiv: 8, - mod: 8, - neg: 8, - bitwise_not_op: 8, - add: 7, - sub: 7, - bitwise_xor_op: 7, - bitwise_or_op: 7, - bitwise_and_op: 7, - bitwise_lshift_op: 7, - bitwise_rshift_op: 7, - filter_op: 6, - concat_op: 5, - match_op: 5, - not_match_op: 5, - regexp_match_op: 5, - not_regexp_match_op: 5, - regexp_replace_op: 5, - ilike_op: 5, - not_ilike_op: 5, - like_op: 5, - not_like_op: 5, - in_op: 5, - not_in_op: 5, - is_: 5, - is_not: 5, - eq: 5, - ne: 5, - is_distinct_from: 5, - is_not_distinct_from: 5, - gt: 5, - lt: 5, - ge: 5, - le: 5, - between_op: 5, - not_between_op: 5, - distinct_op: 5, - inv: 5, - is_true: 5, - is_false: 5, - and_: 3, - or_: 2, - comma_op: -1, - desc_op: 3, - asc_op: 3, - collate: 4, - as_: -1, - exists: 0, - _asbool: -10, -} - - -def is_precedent( - operator: OperatorType, against: Optional[OperatorType] -) -> bool: - if operator is against and is_natural_self_precedent(operator): - return False - elif against is None: - return True - else: - return bool( - _PRECEDENCE.get( - operator, getattr(operator, "precedence", _OpLimit._smallest) - ) - <= _PRECEDENCE.get( - against, getattr(against, "precedence", _OpLimit._largest) - ) - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/roles.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/roles.py deleted file mode 100644 index ae70ac3..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/roles.py +++ /dev/null @@ -1,323 +0,0 @@ -# sql/roles.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -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" - ) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py deleted file mode 100644 index 6d5f941..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/schema.py +++ /dev/null @@ -1,6115 +0,0 @@ -# sql/schema.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""The schema module provides the building blocks for database metadata. - -Each element within this module describes a database entity which can be -created and dropped, or is otherwise part of such an entity. Examples include -tables, columns, sequences, and indexes. - -All entities are subclasses of :class:`~sqlalchemy.schema.SchemaItem`, and as -defined in this module they are intended to be agnostic of any vendor-specific -constructs. - -A collection of entities are grouped into a unit called -:class:`~sqlalchemy.schema.MetaData`. MetaData serves as a logical grouping of -schema elements, and can also be associated with an actual database connection -such that operations involving the contained elements can contact the database -as needed. - -Two of the elements here also build upon their "syntactic" counterparts, which -are defined in :class:`~sqlalchemy.sql.expression.`, specifically -:class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Column`. -Since these objects are part of the SQL expression language, they are usable -as components in SQL expressions. - -""" -from __future__ import annotations - -from abc import ABC -import collections -from enum import Enum -import operator -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import Collection -from typing import Dict -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping -from typing import NoReturn -from typing import Optional -from typing import overload -from typing import Sequence as _typing_Sequence -from typing import Set -from typing import Tuple -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import coercions -from . import ddl -from . import roles -from . import type_api -from . import visitors -from .base import _DefaultDescriptionTuple -from .base import _NoneName -from .base import _SentinelColumnCharacterization -from .base import _SentinelDefaultCharacterization -from .base import DedupeColumnCollection -from .base import DialectKWArgs -from .base import Executable -from .base import SchemaEventTarget as SchemaEventTarget -from .coercions import _document_text_coercion -from .elements import ClauseElement -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import quoted_name -from .elements import TextClause -from .selectable import TableClause -from .type_api import to_instance -from .visitors import ExternallyTraversible -from .visitors import InternalTraversal -from .. import event -from .. import exc -from .. import inspection -from .. import util -from ..util import HasMemoized -from ..util.typing import Final -from ..util.typing import Literal -from ..util.typing import Protocol -from ..util.typing import Self -from ..util.typing import TypedDict -from ..util.typing import TypeGuard - -if typing.TYPE_CHECKING: - from ._typing import _AutoIncrementType - from ._typing import _DDLColumnArgument - from ._typing import _InfoType - from ._typing import _TextCoercedExpressionArgument - from ._typing import _TypeEngineArgument - from .base import ReadOnlyColumnCollection - from .compiler import DDLCompiler - from .elements import BindParameter - from .functions import Function - from .type_api import TypeEngine - from .visitors import _TraverseInternalsType - from .visitors import anon_map - from ..engine import Connection - from ..engine import Engine - from ..engine.interfaces import _CoreMultiExecuteParams - from ..engine.interfaces import CoreExecuteOptionsParameter - from ..engine.interfaces import ExecutionContext - from ..engine.mock import MockConnection - from ..engine.reflection import _ReflectionInfo - from ..sql.selectable import FromClause - -_T = TypeVar("_T", bound="Any") -_SI = TypeVar("_SI", bound="SchemaItem") -_TAB = TypeVar("_TAB", bound="Table") - - -_CreateDropBind = Union["Engine", "Connection", "MockConnection"] - -_ConstraintNameArgument = Optional[Union[str, _NoneName]] - -_ServerDefaultArgument = Union[ - "FetchedValue", str, TextClause, ColumnElement[Any] -] - - -class SchemaConst(Enum): - RETAIN_SCHEMA = 1 - """Symbol indicating that a :class:`_schema.Table`, :class:`.Sequence` - or in some cases a :class:`_schema.ForeignKey` object, in situations - where the object is being copied for a :meth:`.Table.to_metadata` - operation, should retain the schema name that it already has. - - """ - - BLANK_SCHEMA = 2 - """Symbol indicating that a :class:`_schema.Table` or :class:`.Sequence` - should have 'None' for its schema, even if the parent - :class:`_schema.MetaData` has specified a schema. - - .. seealso:: - - :paramref:`_schema.MetaData.schema` - - :paramref:`_schema.Table.schema` - - :paramref:`.Sequence.schema` - - """ - - NULL_UNSPECIFIED = 3 - """Symbol indicating the "nullable" keyword was not passed to a Column. - - This is used to distinguish between the use case of passing - ``nullable=None`` to a :class:`.Column`, which has special meaning - on some backends such as SQL Server. - - """ - - -RETAIN_SCHEMA: Final[Literal[SchemaConst.RETAIN_SCHEMA]] = ( - SchemaConst.RETAIN_SCHEMA -) -BLANK_SCHEMA: Final[Literal[SchemaConst.BLANK_SCHEMA]] = ( - SchemaConst.BLANK_SCHEMA -) -NULL_UNSPECIFIED: Final[Literal[SchemaConst.NULL_UNSPECIFIED]] = ( - SchemaConst.NULL_UNSPECIFIED -) - - -def _get_table_key(name: str, schema: Optional[str]) -> str: - if schema is None: - return name - else: - return schema + "." + name - - -# this should really be in sql/util.py but we'd have to -# break an import cycle -def _copy_expression( - expression: ColumnElement[Any], - source_table: Optional[Table], - target_table: Optional[Table], -) -> ColumnElement[Any]: - if source_table is None or target_table is None: - return expression - - fixed_source_table = source_table - fixed_target_table = target_table - - def replace( - element: ExternallyTraversible, **kw: Any - ) -> Optional[ExternallyTraversible]: - if ( - isinstance(element, Column) - and element.table is fixed_source_table - and element.key in fixed_source_table.c - ): - return fixed_target_table.c[element.key] - else: - return None - - return cast( - ColumnElement[Any], - visitors.replacement_traverse(expression, {}, replace), - ) - - -@inspection._self_inspects -class SchemaItem(SchemaEventTarget, visitors.Visitable): - """Base class for items that define a database schema.""" - - __visit_name__ = "schema_item" - - create_drop_stringify_dialect = "default" - - def _init_items(self, *args: SchemaItem, **kw: Any) -> None: - """Initialize the list of child items for this SchemaItem.""" - for item in args: - if item is not None: - try: - spwd = item._set_parent_with_dispatch - except AttributeError as err: - raise exc.ArgumentError( - "'SchemaItem' object, such as a 'Column' or a " - f"'Constraint' expected, got {item!r}" - ) from err - else: - spwd(self, **kw) - - def __repr__(self) -> str: - return util.generic_repr(self, omit_kwarg=["info"]) - - @util.memoized_property - def info(self) -> _InfoType: - """Info dictionary associated with the object, allowing user-defined - data to be associated with this :class:`.SchemaItem`. - - The dictionary is automatically generated when first accessed. - It can also be specified in the constructor of some objects, - such as :class:`_schema.Table` and :class:`_schema.Column`. - - """ - return {} - - def _schema_item_copy(self, schema_item: _SI) -> _SI: - if "info" in self.__dict__: - schema_item.info = self.info.copy() - schema_item.dispatch._update(self.dispatch) - return schema_item - - _use_schema_map = True - - -class HasConditionalDDL: - """define a class that includes the :meth:`.HasConditionalDDL.ddl_if` - method, allowing for conditional rendering of DDL. - - Currently applies to constraints and indexes. - - .. versionadded:: 2.0 - - - """ - - _ddl_if: Optional[ddl.DDLIf] = None - - def ddl_if( - self, - dialect: Optional[str] = None, - callable_: Optional[ddl.DDLIfCallable] = None, - state: Optional[Any] = None, - ) -> Self: - r"""apply a conditional DDL rule to this schema item. - - These rules work in a similar manner to the - :meth:`.ExecutableDDLElement.execute_if` callable, with the added - feature that the criteria may be checked within the DDL compilation - phase for a construct such as :class:`.CreateTable`. - :meth:`.HasConditionalDDL.ddl_if` currently applies towards the - :class:`.Index` construct as well as all :class:`.Constraint` - constructs. - - :param dialect: string name of a dialect, or a tuple of string names - to indicate multiple dialect types. - - :param callable\_: a callable that is constructed using the same form - as that described in - :paramref:`.ExecutableDDLElement.execute_if.callable_`. - - :param state: any arbitrary object that will be passed to the - callable, if present. - - .. versionadded:: 2.0 - - .. seealso:: - - :ref:`schema_ddl_ddl_if` - background and usage examples - - - """ - self._ddl_if = ddl.DDLIf(dialect, callable_, state) - return self - - -class HasSchemaAttr(SchemaItem): - """schema item that includes a top-level schema name""" - - schema: Optional[str] - - -class Table( - DialectKWArgs, HasSchemaAttr, TableClause, inspection.Inspectable["Table"] -): - r"""Represent a table in a database. - - e.g.:: - - mytable = Table( - "mytable", metadata, - Column('mytable_id', Integer, primary_key=True), - Column('value', String(50)) - ) - - The :class:`_schema.Table` - object constructs a unique instance of itself based - on its name and optional schema name within the given - :class:`_schema.MetaData` object. Calling the :class:`_schema.Table` - constructor with the same name and same :class:`_schema.MetaData` argument - a second time will return the *same* :class:`_schema.Table` - object - in this way - the :class:`_schema.Table` constructor acts as a registry function. - - .. seealso:: - - :ref:`metadata_describing` - Introduction to database metadata - - """ - - __visit_name__ = "table" - - if TYPE_CHECKING: - - @util.ro_non_memoized_property - def primary_key(self) -> PrimaryKeyConstraint: ... - - @util.ro_non_memoized_property - def foreign_keys(self) -> Set[ForeignKey]: ... - - _columns: DedupeColumnCollection[Column[Any]] - - _sentinel_column: Optional[Column[Any]] - - constraints: Set[Constraint] - """A collection of all :class:`_schema.Constraint` objects associated with - this :class:`_schema.Table`. - - Includes :class:`_schema.PrimaryKeyConstraint`, - :class:`_schema.ForeignKeyConstraint`, :class:`_schema.UniqueConstraint`, - :class:`_schema.CheckConstraint`. A separate collection - :attr:`_schema.Table.foreign_key_constraints` refers to the collection - of all :class:`_schema.ForeignKeyConstraint` objects, and the - :attr:`_schema.Table.primary_key` attribute refers to the single - :class:`_schema.PrimaryKeyConstraint` associated with the - :class:`_schema.Table`. - - .. seealso:: - - :attr:`_schema.Table.constraints` - - :attr:`_schema.Table.primary_key` - - :attr:`_schema.Table.foreign_key_constraints` - - :attr:`_schema.Table.indexes` - - :class:`_reflection.Inspector` - - - """ - - indexes: Set[Index] - """A collection of all :class:`_schema.Index` objects associated with this - :class:`_schema.Table`. - - .. seealso:: - - :meth:`_reflection.Inspector.get_indexes` - - """ - - _traverse_internals: _TraverseInternalsType = ( - TableClause._traverse_internals - + [("schema", InternalTraversal.dp_string)] - ) - - if TYPE_CHECKING: - - @util.ro_non_memoized_property - def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]: ... - - @util.ro_non_memoized_property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[str, Column[Any]]: ... - - @util.ro_non_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]: ... - - def _gen_cache_key( - self, anon_map: anon_map, bindparams: List[BindParameter[Any]] - ) -> Tuple[Any, ...]: - if self._annotations: - return (self,) + self._annotations_cache_key - else: - return (self,) - - if not typing.TYPE_CHECKING: - # typing tools seem to be inconsistent in how they handle - # __new__, so suggest this pattern for classes that use - # __new__. apply typing to the __init__ method normally - @util.deprecated_params( - mustexist=( - "1.4", - "Deprecated alias of :paramref:`_schema.Table.must_exist`", - ), - ) - def __new__(cls, *args: Any, **kw: Any) -> Any: - return cls._new(*args, **kw) - - @classmethod - def _new(cls, *args: Any, **kw: Any) -> Any: - if not args and not kw: - # python3k pickle seems to call this - return object.__new__(cls) - - try: - name, metadata, args = args[0], args[1], args[2:] - except IndexError: - raise TypeError( - "Table() takes at least two positional-only " - "arguments 'name' and 'metadata'" - ) - - schema = kw.get("schema", None) - if schema is None: - schema = metadata.schema - elif schema is BLANK_SCHEMA: - schema = None - keep_existing = kw.get("keep_existing", False) - extend_existing = kw.get("extend_existing", False) - - if keep_existing and extend_existing: - msg = "keep_existing and extend_existing are mutually exclusive." - raise exc.ArgumentError(msg) - - must_exist = kw.pop("must_exist", kw.pop("mustexist", False)) - key = _get_table_key(name, schema) - if key in metadata.tables: - if not keep_existing and not extend_existing and bool(args): - raise exc.InvalidRequestError( - f"Table '{key}' is already defined for this MetaData " - "instance. Specify 'extend_existing=True' " - "to redefine " - "options and columns on an " - "existing Table object." - ) - table = metadata.tables[key] - if extend_existing: - table._init_existing(*args, **kw) - return table - else: - if must_exist: - raise exc.InvalidRequestError(f"Table '{key}' not defined") - table = object.__new__(cls) - table.dispatch.before_parent_attach(table, metadata) - metadata._add_table(name, schema, table) - try: - table.__init__(name, metadata, *args, _no_init=False, **kw) - table.dispatch.after_parent_attach(table, metadata) - return table - except Exception: - with util.safe_reraise(): - metadata._remove_table(name, schema) - - def __init__( - self, - name: str, - metadata: MetaData, - *args: SchemaItem, - schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None, - quote: Optional[bool] = None, - quote_schema: Optional[bool] = None, - autoload_with: Optional[Union[Engine, Connection]] = None, - autoload_replace: bool = True, - keep_existing: bool = False, - extend_existing: bool = False, - resolve_fks: bool = True, - include_columns: Optional[Collection[str]] = None, - implicit_returning: bool = True, - comment: Optional[str] = None, - info: Optional[Dict[Any, Any]] = None, - listeners: Optional[ - _typing_Sequence[Tuple[str, Callable[..., Any]]] - ] = None, - prefixes: Optional[_typing_Sequence[str]] = None, - # used internally in the metadata.reflect() process - _extend_on: Optional[Set[Table]] = None, - # used by __new__ to bypass __init__ - _no_init: bool = True, - # dialect-specific keyword args - **kw: Any, - ) -> None: - r"""Constructor for :class:`_schema.Table`. - - - :param name: The name of this table as represented in the database. - - The table name, along with the value of the ``schema`` parameter, - forms a key which uniquely identifies this :class:`_schema.Table` - within - the owning :class:`_schema.MetaData` collection. - Additional calls to :class:`_schema.Table` with the same name, - metadata, - and schema name will return the same :class:`_schema.Table` object. - - Names which contain no upper case characters - will be treated as case insensitive names, and will not be quoted - unless they are a reserved word or contain special characters. - A name with any number of upper case characters is considered - to be case sensitive, and will be sent as quoted. - - To enable unconditional quoting for the table name, specify the flag - ``quote=True`` to the constructor, or use the :class:`.quoted_name` - construct to specify the name. - - :param metadata: a :class:`_schema.MetaData` - object which will contain this - table. The metadata is used as a point of association of this table - with other tables which are referenced via foreign key. It also - may be used to associate this table with a particular - :class:`.Connection` or :class:`.Engine`. - - :param \*args: Additional positional arguments are used primarily - to add the list of :class:`_schema.Column` - objects contained within this - table. Similar to the style of a CREATE TABLE statement, other - :class:`.SchemaItem` constructs may be added here, including - :class:`.PrimaryKeyConstraint`, and - :class:`_schema.ForeignKeyConstraint`. - - :param autoload_replace: Defaults to ``True``; when using - :paramref:`_schema.Table.autoload_with` - in conjunction with :paramref:`_schema.Table.extend_existing`, - indicates - that :class:`_schema.Column` objects present in the already-existing - :class:`_schema.Table` - object should be replaced with columns of the same - name retrieved from the autoload process. When ``False``, columns - already present under existing names will be omitted from the - reflection process. - - Note that this setting does not impact :class:`_schema.Column` objects - specified programmatically within the call to :class:`_schema.Table` - that - also is autoloading; those :class:`_schema.Column` objects will always - replace existing columns of the same name when - :paramref:`_schema.Table.extend_existing` is ``True``. - - .. seealso:: - - :paramref:`_schema.Table.autoload_with` - - :paramref:`_schema.Table.extend_existing` - - :param autoload_with: An :class:`_engine.Engine` or - :class:`_engine.Connection` object, - or a :class:`_reflection.Inspector` object as returned by - :func:`_sa.inspect` - against one, with which this :class:`_schema.Table` - object will be reflected. - When set to a non-None value, the autoload process will take place - for this table against the given engine or connection. - - .. seealso:: - - :ref:`metadata_reflection_toplevel` - - :meth:`_events.DDLEvents.column_reflect` - - :ref:`metadata_reflection_dbagnostic_types` - - :param extend_existing: When ``True``, indicates that if this - :class:`_schema.Table` is already present in the given - :class:`_schema.MetaData`, - apply further arguments within the constructor to the existing - :class:`_schema.Table`. - - If :paramref:`_schema.Table.extend_existing` or - :paramref:`_schema.Table.keep_existing` are not set, - and the given name - of the new :class:`_schema.Table` refers to a :class:`_schema.Table` - that is - already present in the target :class:`_schema.MetaData` collection, - and - this :class:`_schema.Table` - specifies additional columns or other constructs - or flags that modify the table's state, an - error is raised. The purpose of these two mutually-exclusive flags - is to specify what action should be taken when a - :class:`_schema.Table` - is specified that matches an existing :class:`_schema.Table`, - yet specifies - additional constructs. - - :paramref:`_schema.Table.extend_existing` - will also work in conjunction - with :paramref:`_schema.Table.autoload_with` to run a new reflection - operation against the database, even if a :class:`_schema.Table` - of the same name is already present in the target - :class:`_schema.MetaData`; newly reflected :class:`_schema.Column` - objects - and other options will be added into the state of the - :class:`_schema.Table`, potentially overwriting existing columns - and options of the same name. - - As is always the case with :paramref:`_schema.Table.autoload_with`, - :class:`_schema.Column` objects can be specified in the same - :class:`_schema.Table` - constructor, which will take precedence. Below, the existing - table ``mytable`` will be augmented with :class:`_schema.Column` - objects - both reflected from the database, as well as the given - :class:`_schema.Column` - named "y":: - - Table("mytable", metadata, - Column('y', Integer), - extend_existing=True, - autoload_with=engine - ) - - .. seealso:: - - :paramref:`_schema.Table.autoload_with` - - :paramref:`_schema.Table.autoload_replace` - - :paramref:`_schema.Table.keep_existing` - - - :param implicit_returning: True by default - indicates that - RETURNING can be used, typically by the ORM, in order to fetch - server-generated values such as primary key values and - server side defaults, on those backends which support RETURNING. - - In modern SQLAlchemy there is generally no reason to alter this - setting, except for some backend specific cases - (see :ref:`mssql_triggers` in the SQL Server dialect documentation - for one such example). - - :param include_columns: A list of strings indicating a subset of - columns to be loaded via the ``autoload`` operation; table columns who - aren't present in this list will not be represented on the resulting - ``Table`` object. Defaults to ``None`` which indicates all columns - should be reflected. - - :param resolve_fks: Whether or not to reflect :class:`_schema.Table` - objects - related to this one via :class:`_schema.ForeignKey` objects, when - :paramref:`_schema.Table.autoload_with` is - specified. Defaults to True. Set to False to disable reflection of - related tables as :class:`_schema.ForeignKey` - objects are encountered; may be - used either to save on SQL calls or to avoid issues with related tables - that can't be accessed. Note that if a related table is already present - in the :class:`_schema.MetaData` collection, or becomes present later, - a - :class:`_schema.ForeignKey` object associated with this - :class:`_schema.Table` will - resolve to that table normally. - - .. versionadded:: 1.3 - - .. seealso:: - - :paramref:`.MetaData.reflect.resolve_fks` - - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param keep_existing: When ``True``, indicates that if this Table - is already present in the given :class:`_schema.MetaData`, ignore - further arguments within the constructor to the existing - :class:`_schema.Table`, and return the :class:`_schema.Table` - object as - originally created. This is to allow a function that wishes - to define a new :class:`_schema.Table` on first call, but on - subsequent calls will return the same :class:`_schema.Table`, - without any of the declarations (particularly constraints) - being applied a second time. - - If :paramref:`_schema.Table.extend_existing` or - :paramref:`_schema.Table.keep_existing` are not set, - and the given name - of the new :class:`_schema.Table` refers to a :class:`_schema.Table` - that is - already present in the target :class:`_schema.MetaData` collection, - and - this :class:`_schema.Table` - specifies additional columns or other constructs - or flags that modify the table's state, an - error is raised. The purpose of these two mutually-exclusive flags - is to specify what action should be taken when a - :class:`_schema.Table` - is specified that matches an existing :class:`_schema.Table`, - yet specifies - additional constructs. - - .. seealso:: - - :paramref:`_schema.Table.extend_existing` - - :param listeners: A list of tuples of the form ``(<eventname>, <fn>)`` - which will be passed to :func:`.event.listen` upon construction. - This alternate hook to :func:`.event.listen` allows the establishment - of a listener function specific to this :class:`_schema.Table` before - the "autoload" process begins. Historically this has been intended - for use with the :meth:`.DDLEvents.column_reflect` event, however - note that this event hook may now be associated with the - :class:`_schema.MetaData` object directly:: - - def listen_for_reflect(table, column_info): - "handle the column reflection event" - # ... - - t = Table( - 'sometable', - autoload_with=engine, - listeners=[ - ('column_reflect', listen_for_reflect) - ]) - - .. seealso:: - - :meth:`_events.DDLEvents.column_reflect` - - :param must_exist: When ``True``, indicates that this Table must already - be present in the given :class:`_schema.MetaData` collection, else - an exception is raised. - - :param prefixes: - A list of strings to insert after CREATE in the CREATE TABLE - statement. They will be separated by spaces. - - :param quote: Force quoting of this table's name on or off, corresponding - to ``True`` or ``False``. When left at its default of ``None``, - the column identifier will be quoted according to whether the name is - case sensitive (identifiers with at least one upper case character are - treated as case sensitive), or if it's a reserved word. This flag - is only needed to force quoting of a reserved word which is not known - by the SQLAlchemy dialect. - - .. note:: setting this flag to ``False`` will not provide - case-insensitive behavior for table reflection; table reflection - will always search for a mixed-case name in a case sensitive - fashion. Case insensitive names are specified in SQLAlchemy only - by stating the name with all lower case characters. - - :param quote_schema: same as 'quote' but applies to the schema identifier. - - :param schema: The schema name for this table, which is required if - the table resides in a schema other than the default selected schema - for the engine's database connection. Defaults to ``None``. - - If the owning :class:`_schema.MetaData` of this :class:`_schema.Table` - specifies its - own :paramref:`_schema.MetaData.schema` parameter, - then that schema name will - be applied to this :class:`_schema.Table` - if the schema parameter here is set - to ``None``. To set a blank schema name on a :class:`_schema.Table` - that - would otherwise use the schema set on the owning - :class:`_schema.MetaData`, - specify the special symbol :attr:`.BLANK_SCHEMA`. - - The quoting rules for the schema name are the same as those for the - ``name`` parameter, in that quoting is applied for reserved words or - case-sensitive names; to enable unconditional quoting for the schema - name, specify the flag ``quote_schema=True`` to the constructor, or use - the :class:`.quoted_name` construct to specify the name. - - :param comment: Optional string that will render an SQL comment on table - creation. - - .. versionadded:: 1.2 Added the :paramref:`_schema.Table.comment` - parameter - to :class:`_schema.Table`. - - :param \**kw: Additional keyword arguments not mentioned above are - dialect specific, and passed in the form ``<dialectname>_<argname>``. - See the documentation regarding an individual dialect at - :ref:`dialect_toplevel` for detail on documented arguments. - - """ # noqa: E501 - if _no_init: - # don't run __init__ from __new__ by default; - # __new__ has a specific place that __init__ is called - return - - super().__init__(quoted_name(name, quote)) - self.metadata = metadata - - if schema is None: - self.schema = metadata.schema - elif schema is BLANK_SCHEMA: - self.schema = None - else: - quote_schema = quote_schema - assert isinstance(schema, str) - self.schema = quoted_name(schema, quote_schema) - - self._sentinel_column = None - - self.indexes = set() - self.constraints = set() - PrimaryKeyConstraint( - _implicit_generated=True - )._set_parent_with_dispatch(self) - self.foreign_keys = set() # type: ignore - self._extra_dependencies: Set[Table] = set() - if self.schema is not None: - self.fullname = "%s.%s" % (self.schema, self.name) - else: - self.fullname = self.name - - self.implicit_returning = implicit_returning - _reflect_info = kw.pop("_reflect_info", None) - - self.comment = comment - - if info is not None: - self.info = info - - if listeners is not None: - for evt, fn in listeners: - event.listen(self, evt, fn) - - self._prefixes = prefixes if prefixes else [] - - self._extra_kwargs(**kw) - - # load column definitions from the database if 'autoload' is defined - # we do it after the table is in the singleton dictionary to support - # circular foreign keys - if autoload_with is not None: - self._autoload( - metadata, - autoload_with, - include_columns, - _extend_on=_extend_on, - _reflect_info=_reflect_info, - resolve_fks=resolve_fks, - ) - - # initialize all the column, etc. objects. done after reflection to - # allow user-overrides - - self._init_items( - *args, - allow_replacements=extend_existing - or keep_existing - or autoload_with, - all_names={}, - ) - - def _autoload( - self, - metadata: MetaData, - autoload_with: Union[Engine, Connection], - include_columns: Optional[Collection[str]], - exclude_columns: Collection[str] = (), - resolve_fks: bool = True, - _extend_on: Optional[Set[Table]] = None, - _reflect_info: _ReflectionInfo | None = None, - ) -> None: - insp = inspection.inspect(autoload_with) - with insp._inspection_context() as conn_insp: - conn_insp.reflect_table( - self, - include_columns, - exclude_columns, - resolve_fks, - _extend_on=_extend_on, - _reflect_info=_reflect_info, - ) - - @property - def _sorted_constraints(self) -> List[Constraint]: - """Return the set of constraints as a list, sorted by creation - order. - - """ - - return sorted(self.constraints, key=lambda c: c._creation_order) - - @property - def foreign_key_constraints(self) -> Set[ForeignKeyConstraint]: - """:class:`_schema.ForeignKeyConstraint` objects referred to by this - :class:`_schema.Table`. - - This list is produced from the collection of - :class:`_schema.ForeignKey` - objects currently associated. - - - .. seealso:: - - :attr:`_schema.Table.constraints` - - :attr:`_schema.Table.foreign_keys` - - :attr:`_schema.Table.indexes` - - """ - return { - fkc.constraint - for fkc in self.foreign_keys - if fkc.constraint is not None - } - - def _init_existing(self, *args: Any, **kwargs: Any) -> None: - autoload_with = kwargs.pop("autoload_with", None) - autoload = kwargs.pop("autoload", autoload_with is not None) - autoload_replace = kwargs.pop("autoload_replace", True) - schema = kwargs.pop("schema", None) - _extend_on = kwargs.pop("_extend_on", None) - _reflect_info = kwargs.pop("_reflect_info", None) - - # these arguments are only used with _init() - extend_existing = kwargs.pop("extend_existing", False) - keep_existing = kwargs.pop("keep_existing", False) - - assert extend_existing - assert not keep_existing - - if schema and schema != self.schema: - raise exc.ArgumentError( - f"Can't change schema of existing table " - f"from '{self.schema}' to '{schema}'", - ) - - include_columns = kwargs.pop("include_columns", None) - if include_columns is not None: - for c in self.c: - if c.name not in include_columns: - self._columns.remove(c) - - resolve_fks = kwargs.pop("resolve_fks", True) - - for key in ("quote", "quote_schema"): - if key in kwargs: - raise exc.ArgumentError( - "Can't redefine 'quote' or 'quote_schema' arguments" - ) - - # update `self` with these kwargs, if provided - self.comment = kwargs.pop("comment", self.comment) - self.implicit_returning = kwargs.pop( - "implicit_returning", self.implicit_returning - ) - self.info = kwargs.pop("info", self.info) - - exclude_columns: _typing_Sequence[str] - - if autoload: - if not autoload_replace: - # don't replace columns already present. - # we'd like to do this for constraints also however we don't - # have simple de-duping for unnamed constraints. - exclude_columns = [c.name for c in self.c] - else: - exclude_columns = () - self._autoload( - self.metadata, - autoload_with, - include_columns, - exclude_columns, - resolve_fks, - _extend_on=_extend_on, - _reflect_info=_reflect_info, - ) - - all_names = {c.name: c for c in self.c} - self._extra_kwargs(**kwargs) - self._init_items(*args, allow_replacements=True, all_names=all_names) - - def _extra_kwargs(self, **kwargs: Any) -> None: - self._validate_dialect_kwargs(kwargs) - - def _init_collections(self) -> None: - pass - - def _reset_exported(self) -> None: - pass - - @util.ro_non_memoized_property - def _autoincrement_column(self) -> Optional[Column[int]]: - return self.primary_key._autoincrement_column - - @util.ro_memoized_property - def _sentinel_column_characteristics( - self, - ) -> _SentinelColumnCharacterization: - """determine a candidate column (or columns, in case of a client - generated composite primary key) which can be used as an - "insert sentinel" for an INSERT statement. - - The returned structure, :class:`_SentinelColumnCharacterization`, - includes all the details needed by :class:`.Dialect` and - :class:`.SQLCompiler` to determine if these column(s) can be used - as an INSERT..RETURNING sentinel for a particular database - dialect. - - .. versionadded:: 2.0.10 - - """ - - sentinel_is_explicit = False - sentinel_is_autoinc = False - the_sentinel: Optional[_typing_Sequence[Column[Any]]] = None - - # see if a column was explicitly marked "insert_sentinel=True". - explicit_sentinel_col = self._sentinel_column - - if explicit_sentinel_col is not None: - the_sentinel = (explicit_sentinel_col,) - sentinel_is_explicit = True - - autoinc_col = self._autoincrement_column - if sentinel_is_explicit and explicit_sentinel_col is autoinc_col: - assert autoinc_col is not None - sentinel_is_autoinc = True - elif explicit_sentinel_col is None and autoinc_col is not None: - the_sentinel = (autoinc_col,) - sentinel_is_autoinc = True - - default_characterization = _SentinelDefaultCharacterization.UNKNOWN - - if the_sentinel: - the_sentinel_zero = the_sentinel[0] - if the_sentinel_zero.identity: - if the_sentinel_zero.identity._increment_is_negative: - if sentinel_is_explicit: - raise exc.InvalidRequestError( - "Can't use IDENTITY default with negative " - "increment as an explicit sentinel column" - ) - else: - if sentinel_is_autoinc: - autoinc_col = None - sentinel_is_autoinc = False - the_sentinel = None - else: - default_characterization = ( - _SentinelDefaultCharacterization.IDENTITY - ) - elif ( - the_sentinel_zero.default is None - and the_sentinel_zero.server_default is None - ): - if the_sentinel_zero.nullable: - raise exc.InvalidRequestError( - f"Column {the_sentinel_zero} has been marked as a " - "sentinel " - "column with no default generation function; it " - "at least needs to be marked nullable=False assuming " - "user-populated sentinel values will be used." - ) - default_characterization = ( - _SentinelDefaultCharacterization.NONE - ) - elif the_sentinel_zero.default is not None: - if the_sentinel_zero.default.is_sentinel: - default_characterization = ( - _SentinelDefaultCharacterization.SENTINEL_DEFAULT - ) - elif default_is_sequence(the_sentinel_zero.default): - if the_sentinel_zero.default._increment_is_negative: - if sentinel_is_explicit: - raise exc.InvalidRequestError( - "Can't use SEQUENCE default with negative " - "increment as an explicit sentinel column" - ) - else: - if sentinel_is_autoinc: - autoinc_col = None - sentinel_is_autoinc = False - the_sentinel = None - - default_characterization = ( - _SentinelDefaultCharacterization.SEQUENCE - ) - elif the_sentinel_zero.default.is_callable: - default_characterization = ( - _SentinelDefaultCharacterization.CLIENTSIDE - ) - elif the_sentinel_zero.server_default is not None: - if sentinel_is_explicit: - raise exc.InvalidRequestError( - f"Column {the_sentinel[0]} can't be a sentinel column " - "because it uses an explicit server side default " - "that's not the Identity() default." - ) - - default_characterization = ( - _SentinelDefaultCharacterization.SERVERSIDE - ) - - if the_sentinel is None and self.primary_key: - assert autoinc_col is None - - # determine for non-autoincrement pk if all elements are - # client side - for _pkc in self.primary_key: - if _pkc.server_default is not None or ( - _pkc.default and not _pkc.default.is_callable - ): - break - else: - the_sentinel = tuple(self.primary_key) - default_characterization = ( - _SentinelDefaultCharacterization.CLIENTSIDE - ) - - return _SentinelColumnCharacterization( - the_sentinel, - sentinel_is_explicit, - sentinel_is_autoinc, - default_characterization, - ) - - @property - def autoincrement_column(self) -> Optional[Column[int]]: - """Returns the :class:`.Column` object which currently represents - the "auto increment" column, if any, else returns None. - - This is based on the rules for :class:`.Column` as defined by the - :paramref:`.Column.autoincrement` parameter, which generally means the - column within a single integer column primary key constraint that is - not constrained by a foreign key. If the table does not have such - a primary key constraint, then there's no "autoincrement" column. - A :class:`.Table` may have only one column defined as the - "autoincrement" column. - - .. versionadded:: 2.0.4 - - .. seealso:: - - :paramref:`.Column.autoincrement` - - """ - return self._autoincrement_column - - @property - def key(self) -> str: - """Return the 'key' for this :class:`_schema.Table`. - - This value is used as the dictionary key within the - :attr:`_schema.MetaData.tables` collection. It is typically the same - as that of :attr:`_schema.Table.name` for a table with no - :attr:`_schema.Table.schema` - set; otherwise it is typically of the form - ``schemaname.tablename``. - - """ - return _get_table_key(self.name, self.schema) - - def __repr__(self) -> str: - return "Table(%s)" % ", ".join( - [repr(self.name)] - + [repr(self.metadata)] - + [repr(x) for x in self.columns] - + ["%s=%s" % (k, repr(getattr(self, k))) for k in ["schema"]] - ) - - def __str__(self) -> str: - return _get_table_key(self.description, self.schema) - - def add_is_dependent_on(self, table: Table) -> None: - """Add a 'dependency' for this Table. - - This is another Table object which must be created - first before this one can, or dropped after this one. - - Usually, dependencies between tables are determined via - ForeignKey objects. However, for other situations that - create dependencies outside of foreign keys (rules, inheriting), - this method can manually establish such a link. - - """ - self._extra_dependencies.add(table) - - def append_column( - self, column: ColumnClause[Any], replace_existing: bool = False - ) -> None: - """Append a :class:`_schema.Column` to this :class:`_schema.Table`. - - The "key" of the newly added :class:`_schema.Column`, i.e. the - value of its ``.key`` attribute, will then be available - in the ``.c`` collection of this :class:`_schema.Table`, and the - column definition will be included in any CREATE TABLE, SELECT, - UPDATE, etc. statements generated from this :class:`_schema.Table` - construct. - - Note that this does **not** change the definition of the table - as it exists within any underlying database, assuming that - table has already been created in the database. Relational - databases support the addition of columns to existing tables - using the SQL ALTER command, which would need to be - emitted for an already-existing table that doesn't contain - the newly added column. - - :param replace_existing: When ``True``, allows replacing existing - columns. When ``False``, the default, an warning will be raised - if a column with the same ``.key`` already exists. A future - version of sqlalchemy will instead rise a warning. - - .. versionadded:: 1.4.0 - """ - - try: - column._set_parent_with_dispatch( - self, - allow_replacements=replace_existing, - all_names={c.name: c for c in self.c}, - ) - except exc.DuplicateColumnError as de: - raise exc.DuplicateColumnError( - f"{de.args[0]} Specify replace_existing=True to " - "Table.append_column() to replace an " - "existing column." - ) from de - - def append_constraint(self, constraint: Union[Index, Constraint]) -> None: - """Append a :class:`_schema.Constraint` to this - :class:`_schema.Table`. - - This has the effect of the constraint being included in any - future CREATE TABLE statement, assuming specific DDL creation - events have not been associated with the given - :class:`_schema.Constraint` object. - - Note that this does **not** produce the constraint within the - relational database automatically, for a table that already exists - in the database. To add a constraint to an - existing relational database table, the SQL ALTER command must - be used. SQLAlchemy also provides the - :class:`.AddConstraint` construct which can produce this SQL when - invoked as an executable clause. - - """ - - constraint._set_parent_with_dispatch(self) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - metadata = parent - assert isinstance(metadata, MetaData) - metadata._add_table(self.name, self.schema, self) - self.metadata = metadata - - def create(self, bind: _CreateDropBind, checkfirst: bool = False) -> None: - """Issue a ``CREATE`` statement for this - :class:`_schema.Table`, using the given - :class:`.Connection` or :class:`.Engine` - for connectivity. - - .. seealso:: - - :meth:`_schema.MetaData.create_all`. - - """ - - bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst) - - def drop(self, bind: _CreateDropBind, checkfirst: bool = False) -> None: - """Issue a ``DROP`` statement for this - :class:`_schema.Table`, using the given - :class:`.Connection` or :class:`.Engine` for connectivity. - - .. seealso:: - - :meth:`_schema.MetaData.drop_all`. - - """ - bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst) - - @util.deprecated( - "1.4", - ":meth:`_schema.Table.tometadata` is renamed to " - ":meth:`_schema.Table.to_metadata`", - ) - def tometadata( - self, - metadata: MetaData, - schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA, - referred_schema_fn: Optional[ - Callable[ - [Table, Optional[str], ForeignKeyConstraint, Optional[str]], - Optional[str], - ] - ] = None, - name: Optional[str] = None, - ) -> Table: - """Return a copy of this :class:`_schema.Table` - associated with a different - :class:`_schema.MetaData`. - - See :meth:`_schema.Table.to_metadata` for a full description. - - """ - return self.to_metadata( - metadata, - schema=schema, - referred_schema_fn=referred_schema_fn, - name=name, - ) - - def to_metadata( - self, - metadata: MetaData, - schema: Union[str, Literal[SchemaConst.RETAIN_SCHEMA]] = RETAIN_SCHEMA, - referred_schema_fn: Optional[ - Callable[ - [Table, Optional[str], ForeignKeyConstraint, Optional[str]], - Optional[str], - ] - ] = None, - name: Optional[str] = None, - ) -> Table: - """Return a copy of this :class:`_schema.Table` associated with a - different :class:`_schema.MetaData`. - - E.g.:: - - m1 = MetaData() - - user = Table('user', m1, Column('id', Integer, primary_key=True)) - - m2 = MetaData() - user_copy = user.to_metadata(m2) - - .. versionchanged:: 1.4 The :meth:`_schema.Table.to_metadata` function - was renamed from :meth:`_schema.Table.tometadata`. - - - :param metadata: Target :class:`_schema.MetaData` object, - into which the - new :class:`_schema.Table` object will be created. - - :param schema: optional string name indicating the target schema. - Defaults to the special symbol :attr:`.RETAIN_SCHEMA` which indicates - that no change to the schema name should be made in the new - :class:`_schema.Table`. If set to a string name, the new - :class:`_schema.Table` - will have this new name as the ``.schema``. If set to ``None``, the - schema will be set to that of the schema set on the target - :class:`_schema.MetaData`, which is typically ``None`` as well, - unless - set explicitly:: - - m2 = MetaData(schema='newschema') - - # user_copy_one will have "newschema" as the schema name - user_copy_one = user.to_metadata(m2, schema=None) - - m3 = MetaData() # schema defaults to None - - # user_copy_two will have None as the schema name - user_copy_two = user.to_metadata(m3, schema=None) - - :param referred_schema_fn: optional callable which can be supplied - in order to provide for the schema name that should be assigned - to the referenced table of a :class:`_schema.ForeignKeyConstraint`. - The callable accepts this parent :class:`_schema.Table`, the - target schema that we are changing to, the - :class:`_schema.ForeignKeyConstraint` object, and the existing - "target schema" of that constraint. The function should return the - string schema name that should be applied. To reset the schema - to "none", return the symbol :data:`.BLANK_SCHEMA`. To effect no - change, return ``None`` or :data:`.RETAIN_SCHEMA`. - - .. versionchanged:: 1.4.33 The ``referred_schema_fn`` function - may return the :data:`.BLANK_SCHEMA` or :data:`.RETAIN_SCHEMA` - symbols. - - E.g.:: - - def referred_schema_fn(table, to_schema, - constraint, referred_schema): - if referred_schema == 'base_tables': - return referred_schema - else: - return to_schema - - new_table = table.to_metadata(m2, schema="alt_schema", - referred_schema_fn=referred_schema_fn) - - :param name: optional string name indicating the target table name. - If not specified or None, the table name is retained. This allows - a :class:`_schema.Table` to be copied to the same - :class:`_schema.MetaData` target - with a new name. - - """ - if name is None: - name = self.name - - actual_schema: Optional[str] - - if schema is RETAIN_SCHEMA: - actual_schema = self.schema - elif schema is None: - actual_schema = metadata.schema - else: - actual_schema = schema - key = _get_table_key(name, actual_schema) - if key in metadata.tables: - util.warn( - f"Table '{self.description}' already exists within the given " - "MetaData - not copying." - ) - return metadata.tables[key] - - args = [] - for col in self.columns: - args.append(col._copy(schema=actual_schema)) - table = Table( - name, - metadata, - schema=actual_schema, - comment=self.comment, - *args, - **self.kwargs, - ) - for const in self.constraints: - if isinstance(const, ForeignKeyConstraint): - referred_schema = const._referred_schema - if referred_schema_fn: - fk_constraint_schema = referred_schema_fn( - self, actual_schema, const, referred_schema - ) - else: - fk_constraint_schema = ( - actual_schema - if referred_schema == self.schema - else None - ) - table.append_constraint( - const._copy( - schema=fk_constraint_schema, target_table=table - ) - ) - elif not const._type_bound: - # skip unique constraints that would be generated - # by the 'unique' flag on Column - if const._column_flag: - continue - - table.append_constraint( - const._copy(schema=actual_schema, target_table=table) - ) - for index in self.indexes: - # skip indexes that would be generated - # by the 'index' flag on Column - if index._column_flag: - continue - Index( - index.name, - unique=index.unique, - *[ - _copy_expression(expr, self, table) - for expr in index._table_bound_expressions - ], - _table=table, - **index.kwargs, - ) - return self._schema_item_copy(table) - - -class Column(DialectKWArgs, SchemaItem, ColumnClause[_T]): - """Represents a column in a database table.""" - - __visit_name__ = "column" - - inherit_cache = True - key: str - - server_default: Optional[FetchedValue] - - def __init__( - self, - __name_pos: Optional[ - Union[str, _TypeEngineArgument[_T], SchemaEventTarget] - ] = None, - __type_pos: Optional[ - Union[_TypeEngineArgument[_T], SchemaEventTarget] - ] = None, - *args: SchemaEventTarget, - name: Optional[str] = None, - type_: Optional[_TypeEngineArgument[_T]] = None, - autoincrement: _AutoIncrementType = "auto", - default: Optional[Any] = None, - doc: Optional[str] = None, - key: Optional[str] = None, - index: Optional[bool] = None, - unique: Optional[bool] = None, - info: Optional[_InfoType] = None, - nullable: Optional[ - Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]] - ] = SchemaConst.NULL_UNSPECIFIED, - onupdate: Optional[Any] = None, - primary_key: bool = False, - server_default: Optional[_ServerDefaultArgument] = None, - server_onupdate: Optional[FetchedValue] = None, - quote: Optional[bool] = None, - system: bool = False, - comment: Optional[str] = None, - insert_sentinel: bool = False, - _omit_from_statements: bool = False, - _proxies: Optional[Any] = None, - **dialect_kwargs: Any, - ): - r""" - Construct a new ``Column`` object. - - :param name: The name of this column as represented in the database. - This argument may be the first positional argument, or specified - via keyword. - - Names which contain no upper case characters - will be treated as case insensitive names, and will not be quoted - unless they are a reserved word. Names with any number of upper - case characters will be quoted and sent exactly. Note that this - behavior applies even for databases which standardize upper - case names as case insensitive such as Oracle. - - The name field may be omitted at construction time and applied - later, at any time before the Column is associated with a - :class:`_schema.Table`. This is to support convenient - usage within the :mod:`~sqlalchemy.ext.declarative` extension. - - :param type\_: The column's type, indicated using an instance which - subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments - are required for the type, the class of the type can be sent - as well, e.g.:: - - # use a type with arguments - Column('data', String(50)) - - # use no arguments - Column('level', Integer) - - The ``type`` argument may be the second positional argument - or specified by keyword. - - If the ``type`` is ``None`` or is omitted, it will first default to - the special type :class:`.NullType`. If and when this - :class:`_schema.Column` is made to refer to another column using - :class:`_schema.ForeignKey` and/or - :class:`_schema.ForeignKeyConstraint`, the type - of the remote-referenced column will be copied to this column as - well, at the moment that the foreign key is resolved against that - remote :class:`_schema.Column` object. - - :param \*args: Additional positional arguments include various - :class:`.SchemaItem` derived constructs which will be applied - as options to the column. These include instances of - :class:`.Constraint`, :class:`_schema.ForeignKey`, - :class:`.ColumnDefault`, :class:`.Sequence`, :class:`.Computed` - :class:`.Identity`. In some cases an - equivalent keyword argument is available such as ``server_default``, - ``default`` and ``unique``. - - :param autoincrement: Set up "auto increment" semantics for an - **integer primary key column with no foreign key dependencies** - (see later in this docstring for a more specific definition). - This may influence the :term:`DDL` that will be emitted for - this column during a table create, as well as how the column - will be considered when INSERT statements are compiled and - executed. - - The default value is the string ``"auto"``, - which indicates that a single-column (i.e. non-composite) primary key - that is of an INTEGER type with no other client-side or server-side - default constructs indicated should receive auto increment semantics - automatically. Other values include ``True`` (force this column to - have auto-increment semantics for a :term:`composite primary key` as - well), ``False`` (this column should never have auto-increment - semantics), and the string ``"ignore_fk"`` (special-case for foreign - key columns, see below). - - The term "auto increment semantics" refers both to the kind of DDL - that will be emitted for the column within a CREATE TABLE statement, - when methods such as :meth:`.MetaData.create_all` and - :meth:`.Table.create` are invoked, as well as how the column will be - considered when an INSERT statement is compiled and emitted to the - database: - - * **DDL rendering** (i.e. :meth:`.MetaData.create_all`, - :meth:`.Table.create`): When used on a :class:`.Column` that has - no other - default-generating construct associated with it (such as a - :class:`.Sequence` or :class:`.Identity` construct), the parameter - will imply that database-specific keywords such as PostgreSQL - ``SERIAL``, MySQL ``AUTO_INCREMENT``, or ``IDENTITY`` on SQL Server - should also be rendered. Not every database backend has an - "implied" default generator available; for example the Oracle - backend always needs an explicit construct such as - :class:`.Identity` to be included with a :class:`.Column` in order - for the DDL rendered to include auto-generating constructs to also - be produced in the database. - - * **INSERT semantics** (i.e. when a :func:`_sql.insert` construct is - compiled into a SQL string and is then executed on a database using - :meth:`_engine.Connection.execute` or equivalent): A single-row - INSERT statement will be known to produce a new integer primary key - value automatically for this column, which will be accessible - after the statement is invoked via the - :attr:`.CursorResult.inserted_primary_key` attribute upon the - :class:`_result.Result` object. This also applies towards use of the - ORM when ORM-mapped objects are persisted to the database, - indicating that a new integer primary key will be available to - become part of the :term:`identity key` for that object. This - behavior takes place regardless of what DDL constructs are - associated with the :class:`_schema.Column` and is independent - of the "DDL Rendering" behavior discussed in the previous note - above. - - The parameter may be set to ``True`` to indicate that a column which - is part of a composite (i.e. multi-column) primary key should - have autoincrement semantics, though note that only one column - within a primary key may have this setting. It can also - be set to ``True`` to indicate autoincrement semantics on a - column that has a client-side or server-side default configured, - however note that not all dialects can accommodate all styles - of default as an "autoincrement". It can also be - set to ``False`` on a single-column primary key that has a - datatype of INTEGER in order to disable auto increment semantics - for that column. - - The setting *only* has an effect for columns which are: - - * Integer derived (i.e. INT, SMALLINT, BIGINT). - - * Part of the primary key - - * Not referring to another column via :class:`_schema.ForeignKey`, - unless - the value is specified as ``'ignore_fk'``:: - - # turn on autoincrement for this column despite - # the ForeignKey() - Column('id', ForeignKey('other.id'), - primary_key=True, autoincrement='ignore_fk') - - It is typically not desirable to have "autoincrement" enabled on a - column that refers to another via foreign key, as such a column is - required to refer to a value that originates from elsewhere. - - The setting has these effects on columns that meet the - above criteria: - - * DDL issued for the column, if the column does not already include - a default generating construct supported by the backend such as - :class:`.Identity`, will include database-specific - keywords intended to signify this column as an - "autoincrement" column for specific backends. Behavior for - primary SQLAlchemy dialects includes: - - * AUTO INCREMENT on MySQL and MariaDB - * SERIAL on PostgreSQL - * IDENTITY on MS-SQL - this occurs even without the - :class:`.Identity` construct as the - :paramref:`.Column.autoincrement` parameter pre-dates this - construct. - * SQLite - SQLite integer primary key columns are implicitly - "auto incrementing" and no additional keywords are rendered; - to render the special SQLite keyword ``AUTOINCREMENT`` - is not included as this is unnecessary and not recommended - by the database vendor. See the section - :ref:`sqlite_autoincrement` for more background. - * Oracle - The Oracle dialect has no default "autoincrement" - feature available at this time, instead the :class:`.Identity` - construct is recommended to achieve this (the :class:`.Sequence` - construct may also be used). - * Third-party dialects - consult those dialects' documentation - for details on their specific behaviors. - - * When a single-row :func:`_sql.insert` construct is compiled and - executed, which does not set the :meth:`_sql.Insert.inline` - modifier, newly generated primary key values for this column - will be automatically retrieved upon statement execution - using a method specific to the database driver in use: - - * MySQL, SQLite - calling upon ``cursor.lastrowid()`` - (see - `https://www.python.org/dev/peps/pep-0249/#lastrowid - <https://www.python.org/dev/peps/pep-0249/#lastrowid>`_) - * PostgreSQL, SQL Server, Oracle - use RETURNING or an equivalent - construct when rendering an INSERT statement, and then retrieving - the newly generated primary key values after execution - * PostgreSQL, Oracle for :class:`_schema.Table` objects that - set :paramref:`_schema.Table.implicit_returning` to False - - for a :class:`.Sequence` only, the :class:`.Sequence` is invoked - explicitly before the INSERT statement takes place so that the - newly generated primary key value is available to the client - * SQL Server for :class:`_schema.Table` objects that - set :paramref:`_schema.Table.implicit_returning` to False - - the ``SELECT scope_identity()`` construct is used after the - INSERT statement is invoked to retrieve the newly generated - primary key value. - * Third-party dialects - consult those dialects' documentation - for details on their specific behaviors. - - * For multiple-row :func:`_sql.insert` constructs invoked with - a list of parameters (i.e. "executemany" semantics), primary-key - retrieving behaviors are generally disabled, however there may - be special APIs that may be used to retrieve lists of new - primary key values for an "executemany", such as the psycopg2 - "fast insertmany" feature. Such features are very new and - may not yet be well covered in documentation. - - :param default: A scalar, Python callable, or - :class:`_expression.ColumnElement` expression representing the - *default value* for this column, which will be invoked upon insert - if this column is otherwise not specified in the VALUES clause of - the insert. This is a shortcut to using :class:`.ColumnDefault` as - a positional argument; see that class for full detail on the - structure of the argument. - - Contrast this argument to - :paramref:`_schema.Column.server_default` - which creates a default generator on the database side. - - .. seealso:: - - :ref:`metadata_defaults_toplevel` - - :param doc: optional String that can be used by the ORM or similar - to document attributes on the Python side. This attribute does - **not** render SQL comments; use the - :paramref:`_schema.Column.comment` - parameter for this purpose. - - :param key: An optional string identifier which will identify this - ``Column`` object on the :class:`_schema.Table`. - When a key is provided, - this is the only identifier referencing the ``Column`` within the - application, including ORM attribute mapping; the ``name`` field - is used only when rendering SQL. - - :param index: When ``True``, indicates that a :class:`_schema.Index` - construct will be automatically generated for this - :class:`_schema.Column`, which will result in a "CREATE INDEX" - statement being emitted for the :class:`_schema.Table` when the DDL - create operation is invoked. - - Using this flag is equivalent to making use of the - :class:`_schema.Index` construct explicitly at the level of the - :class:`_schema.Table` construct itself:: - - Table( - "some_table", - metadata, - Column("x", Integer), - Index("ix_some_table_x", "x") - ) - - To add the :paramref:`_schema.Index.unique` flag to the - :class:`_schema.Index`, set both the - :paramref:`_schema.Column.unique` and - :paramref:`_schema.Column.index` flags to True simultaneously, - which will have the effect of rendering the "CREATE UNIQUE INDEX" - DDL instruction instead of "CREATE INDEX". - - The name of the index is generated using the - :ref:`default naming convention <constraint_default_naming_convention>` - which for the :class:`_schema.Index` construct is of the form - ``ix_<tablename>_<columnname>``. - - As this flag is intended only as a convenience for the common case - of adding a single-column, default configured index to a table - definition, explicit use of the :class:`_schema.Index` construct - should be preferred for most use cases, including composite indexes - that encompass more than one column, indexes with SQL expressions - or ordering, backend-specific index configuration options, and - indexes that use a specific name. - - .. note:: the :attr:`_schema.Column.index` attribute on - :class:`_schema.Column` - **does not indicate** if this column is indexed or not, only - if this flag was explicitly set here. To view indexes on - a column, view the :attr:`_schema.Table.indexes` collection - or use :meth:`_reflection.Inspector.get_indexes`. - - .. seealso:: - - :ref:`schema_indexes` - - :ref:`constraint_naming_conventions` - - :paramref:`_schema.Column.unique` - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param nullable: When set to ``False``, will cause the "NOT NULL" - phrase to be added when generating DDL for the column. When - ``True``, will normally generate nothing (in SQL this defaults to - "NULL"), except in some very specific backend-specific edge cases - where "NULL" may render explicitly. - Defaults to ``True`` unless :paramref:`_schema.Column.primary_key` - is also ``True`` or the column specifies a :class:`_sql.Identity`, - in which case it defaults to ``False``. - This parameter is only used when issuing CREATE TABLE statements. - - .. note:: - - When the column specifies a :class:`_sql.Identity` this - parameter is in general ignored by the DDL compiler. The - PostgreSQL database allows nullable identity column by - setting this parameter to ``True`` explicitly. - - :param onupdate: A scalar, Python callable, or - :class:`~sqlalchemy.sql.expression.ClauseElement` representing a - default value to be applied to the column within UPDATE - statements, which will be invoked upon update if this column is not - present in the SET clause of the update. This is a shortcut to - using :class:`.ColumnDefault` as a positional argument with - ``for_update=True``. - - .. seealso:: - - :ref:`metadata_defaults` - complete discussion of onupdate - - :param primary_key: If ``True``, marks this column as a primary key - column. Multiple columns can have this flag set to specify - composite primary keys. As an alternative, the primary key of a - :class:`_schema.Table` can be specified via an explicit - :class:`.PrimaryKeyConstraint` object. - - :param server_default: A :class:`.FetchedValue` instance, str, Unicode - or :func:`~sqlalchemy.sql.expression.text` construct representing - the DDL DEFAULT value for the column. - - String types will be emitted as-is, surrounded by single quotes:: - - Column('x', Text, server_default="val") - - x TEXT DEFAULT 'val' - - A :func:`~sqlalchemy.sql.expression.text` expression will be - rendered as-is, without quotes:: - - Column('y', DateTime, server_default=text('NOW()')) - - y DATETIME DEFAULT NOW() - - Strings and text() will be converted into a - :class:`.DefaultClause` object upon initialization. - - This parameter can also accept complex combinations of contextually - valid SQLAlchemy expressions or constructs:: - - from sqlalchemy import create_engine - from sqlalchemy import Table, Column, MetaData, ARRAY, Text - from sqlalchemy.dialects.postgresql import array - - engine = create_engine( - 'postgresql+psycopg2://scott:tiger@localhost/mydatabase' - ) - metadata_obj = MetaData() - tbl = Table( - "foo", - metadata_obj, - Column("bar", - ARRAY(Text), - server_default=array(["biz", "bang", "bash"]) - ) - ) - metadata_obj.create_all(engine) - - The above results in a table created with the following SQL:: - - CREATE TABLE foo ( - bar TEXT[] DEFAULT ARRAY['biz', 'bang', 'bash'] - ) - - Use :class:`.FetchedValue` to indicate that an already-existing - column will generate a default value on the database side which - will be available to SQLAlchemy for post-fetch after inserts. This - construct does not specify any DDL and the implementation is left - to the database, such as via a trigger. - - .. seealso:: - - :ref:`server_defaults` - complete discussion of server side - defaults - - :param server_onupdate: A :class:`.FetchedValue` instance - representing a database-side default generation function, - such as a trigger. This - indicates to SQLAlchemy that a newly generated value will be - available after updates. This construct does not actually - implement any kind of generation function within the database, - which instead must be specified separately. - - - .. warning:: This directive **does not** currently produce MySQL's - "ON UPDATE CURRENT_TIMESTAMP()" clause. See - :ref:`mysql_timestamp_onupdate` for background on how to - produce this clause. - - .. seealso:: - - :ref:`triggered_columns` - - :param quote: Force quoting of this column's name on or off, - corresponding to ``True`` or ``False``. When left at its default - of ``None``, the column identifier will be quoted according to - whether the name is case sensitive (identifiers with at least one - upper case character are treated as case sensitive), or if it's a - reserved word. This flag is only needed to force quoting of a - reserved word which is not known by the SQLAlchemy dialect. - - :param unique: When ``True``, and the :paramref:`_schema.Column.index` - parameter is left at its default value of ``False``, - indicates that a :class:`_schema.UniqueConstraint` - construct will be automatically generated for this - :class:`_schema.Column`, - which will result in a "UNIQUE CONSTRAINT" clause referring - to this column being included - in the ``CREATE TABLE`` statement emitted, when the DDL create - operation for the :class:`_schema.Table` object is invoked. - - When this flag is ``True`` while the - :paramref:`_schema.Column.index` parameter is simultaneously - set to ``True``, the effect instead is that a - :class:`_schema.Index` construct which includes the - :paramref:`_schema.Index.unique` parameter set to ``True`` - is generated. See the documentation for - :paramref:`_schema.Column.index` for additional detail. - - Using this flag is equivalent to making use of the - :class:`_schema.UniqueConstraint` construct explicitly at the - level of the :class:`_schema.Table` construct itself:: - - Table( - "some_table", - metadata, - Column("x", Integer), - UniqueConstraint("x") - ) - - The :paramref:`_schema.UniqueConstraint.name` parameter - of the unique constraint object is left at its default value - of ``None``; in the absence of a :ref:`naming convention <constraint_naming_conventions>` - for the enclosing :class:`_schema.MetaData`, the UNIQUE CONSTRAINT - construct will be emitted as unnamed, which typically invokes - a database-specific naming convention to take place. - - As this flag is intended only as a convenience for the common case - of adding a single-column, default configured unique constraint to a table - definition, explicit use of the :class:`_schema.UniqueConstraint` construct - should be preferred for most use cases, including composite constraints - that encompass more than one column, backend-specific index configuration options, and - constraints that use a specific name. - - .. note:: the :attr:`_schema.Column.unique` attribute on - :class:`_schema.Column` - **does not indicate** if this column has a unique constraint or - not, only if this flag was explicitly set here. To view - indexes and unique constraints that may involve this column, - view the - :attr:`_schema.Table.indexes` and/or - :attr:`_schema.Table.constraints` collections or use - :meth:`_reflection.Inspector.get_indexes` and/or - :meth:`_reflection.Inspector.get_unique_constraints` - - .. seealso:: - - :ref:`schema_unique_constraint` - - :ref:`constraint_naming_conventions` - - :paramref:`_schema.Column.index` - - :param system: When ``True``, indicates this is a "system" column, - that is a column which is automatically made available by the - database, and should not be included in the columns list for a - ``CREATE TABLE`` statement. - - For more elaborate scenarios where columns should be - conditionally rendered differently on different backends, - consider custom compilation rules for :class:`.CreateColumn`. - - :param comment: Optional string that will render an SQL comment on - table creation. - - .. versionadded:: 1.2 Added the - :paramref:`_schema.Column.comment` - parameter to :class:`_schema.Column`. - - :param insert_sentinel: Marks this :class:`_schema.Column` as an - :term:`insert sentinel` used for optimizing the performance of the - :term:`insertmanyvalues` feature for tables that don't - otherwise have qualifying primary key configurations. - - .. versionadded:: 2.0.10 - - .. seealso:: - - :func:`_schema.insert_sentinel` - all in one helper for declaring - sentinel columns - - :ref:`engine_insertmanyvalues` - - :ref:`engine_insertmanyvalues_sentinel_columns` - - - """ # noqa: E501, RST201, RST202 - - l_args = [__name_pos, __type_pos] + list(args) - del args - - if l_args: - if isinstance(l_args[0], str): - if name is not None: - raise exc.ArgumentError( - "May not pass name positionally and as a keyword." - ) - name = l_args.pop(0) # type: ignore - elif l_args[0] is None: - l_args.pop(0) - if l_args: - coltype = l_args[0] - - if hasattr(coltype, "_sqla_type"): - if type_ is not None: - raise exc.ArgumentError( - "May not pass type_ positionally and as a keyword." - ) - type_ = l_args.pop(0) # type: ignore - elif l_args[0] is None: - l_args.pop(0) - - if name is not None: - name = quoted_name(name, quote) - elif quote is not None: - raise exc.ArgumentError( - "Explicit 'name' is required when sending 'quote' argument" - ) - - # name = None is expected to be an interim state - # note this use case is legacy now that ORM declarative has a - # dedicated "column" construct local to the ORM - super().__init__(name, type_) # type: ignore - - self.key = key if key is not None else name # type: ignore - self.primary_key = primary_key - self._insert_sentinel = insert_sentinel - self._omit_from_statements = _omit_from_statements - self._user_defined_nullable = udn = nullable - if udn is not NULL_UNSPECIFIED: - self.nullable = udn - else: - self.nullable = not primary_key - - # these default to None because .index and .unique is *not* - # an informational flag about Column - there can still be an - # Index or UniqueConstraint referring to this Column. - self.index = index - self.unique = unique - - self.system = system - self.doc = doc - self.autoincrement: _AutoIncrementType = autoincrement - self.constraints = set() - self.foreign_keys = set() - self.comment = comment - self.computed = None - self.identity = None - - # check if this Column is proxying another column - - if _proxies is not None: - self._proxies = _proxies - else: - # otherwise, add DDL-related events - self._set_type(self.type) - - if default is not None: - if not isinstance(default, (ColumnDefault, Sequence)): - default = ColumnDefault(default) - - self.default = default - l_args.append(default) - else: - self.default = None - - if onupdate is not None: - if not isinstance(onupdate, (ColumnDefault, Sequence)): - onupdate = ColumnDefault(onupdate, for_update=True) - - self.onupdate = onupdate - l_args.append(onupdate) - else: - self.onupdate = None - - if server_default is not None: - if isinstance(server_default, FetchedValue): - server_default = server_default._as_for_update(False) - l_args.append(server_default) - else: - server_default = DefaultClause(server_default) - l_args.append(server_default) - self.server_default = server_default - - if server_onupdate is not None: - if isinstance(server_onupdate, FetchedValue): - server_onupdate = server_onupdate._as_for_update(True) - l_args.append(server_onupdate) - else: - server_onupdate = DefaultClause( - server_onupdate, for_update=True - ) - l_args.append(server_onupdate) - self.server_onupdate = server_onupdate - - self._init_items(*cast(_typing_Sequence[SchemaItem], l_args)) - - util.set_creation_order(self) - - if info is not None: - self.info = info - - self._extra_kwargs(**dialect_kwargs) - - table: Table - - constraints: Set[Constraint] - - foreign_keys: Set[ForeignKey] - """A collection of all :class:`_schema.ForeignKey` marker objects - associated with this :class:`_schema.Column`. - - Each object is a member of a :class:`_schema.Table`-wide - :class:`_schema.ForeignKeyConstraint`. - - .. seealso:: - - :attr:`_schema.Table.foreign_keys` - - """ - - index: Optional[bool] - """The value of the :paramref:`_schema.Column.index` parameter. - - Does not indicate if this :class:`_schema.Column` is actually indexed - or not; use :attr:`_schema.Table.indexes`. - - .. seealso:: - - :attr:`_schema.Table.indexes` - """ - - unique: Optional[bool] - """The value of the :paramref:`_schema.Column.unique` parameter. - - Does not indicate if this :class:`_schema.Column` is actually subject to - a unique constraint or not; use :attr:`_schema.Table.indexes` and - :attr:`_schema.Table.constraints`. - - .. seealso:: - - :attr:`_schema.Table.indexes` - - :attr:`_schema.Table.constraints`. - - """ - - computed: Optional[Computed] - - identity: Optional[Identity] - - def _set_type(self, type_: TypeEngine[Any]) -> None: - assert self.type._isnull or type_ is self.type - - self.type = type_ - if isinstance(self.type, SchemaEventTarget): - self.type._set_parent_with_dispatch(self) - for impl in self.type._variant_mapping.values(): - if isinstance(impl, SchemaEventTarget): - impl._set_parent_with_dispatch(self) - - @HasMemoized.memoized_attribute - def _default_description_tuple(self) -> _DefaultDescriptionTuple: - """used by default.py -> _process_execute_defaults()""" - - return _DefaultDescriptionTuple._from_column_default(self.default) - - @HasMemoized.memoized_attribute - def _onupdate_description_tuple(self) -> _DefaultDescriptionTuple: - """used by default.py -> _process_execute_defaults()""" - return _DefaultDescriptionTuple._from_column_default(self.onupdate) - - @util.memoized_property - def _gen_static_annotations_cache_key(self) -> bool: # type: ignore - """special attribute used by cache key gen, if true, we will - use a static cache key for the annotations dictionary, else we - will generate a new cache key for annotations each time. - - Added for #8790 - - """ - return self.table is not None and self.table._is_table - - def _extra_kwargs(self, **kwargs: Any) -> None: - self._validate_dialect_kwargs(kwargs) - - def __str__(self) -> str: - if self.name is None: - return "(no name)" - elif self.table is not None: - if self.table.named_with_column: - return self.table.description + "." + self.description - else: - return self.description - else: - return self.description - - def references(self, column: Column[Any]) -> bool: - """Return True if this Column references the given column via foreign - key.""" - - for fk in self.foreign_keys: - if fk.column.proxy_set.intersection(column.proxy_set): - return True - else: - return False - - def append_foreign_key(self, fk: ForeignKey) -> None: - fk._set_parent_with_dispatch(self) - - def __repr__(self) -> str: - kwarg = [] - if self.key != self.name: - kwarg.append("key") - if self.primary_key: - kwarg.append("primary_key") - if not self.nullable: - kwarg.append("nullable") - if self.onupdate: - kwarg.append("onupdate") - if self.default: - kwarg.append("default") - if self.server_default: - kwarg.append("server_default") - if self.comment: - kwarg.append("comment") - return "Column(%s)" % ", ".join( - [repr(self.name)] - + [repr(self.type)] - + [repr(x) for x in self.foreign_keys if x is not None] - + [repr(x) for x in self.constraints] - + [ - ( - self.table is not None - and "table=<%s>" % self.table.description - or "table=None" - ) - ] - + ["%s=%s" % (k, repr(getattr(self, k))) for k in kwarg] - ) - - def _set_parent( # type: ignore[override] - self, - parent: SchemaEventTarget, - *, - all_names: Dict[str, Column[Any]], - allow_replacements: bool, - **kw: Any, - ) -> None: - table = parent - assert isinstance(table, Table) - if not self.name: - raise exc.ArgumentError( - "Column must be constructed with a non-blank name or " - "assign a non-blank .name before adding to a Table." - ) - - self._reset_memoizations() - - if self.key is None: - self.key = self.name - - existing = getattr(self, "table", None) - if existing is not None and existing is not table: - raise exc.ArgumentError( - f"Column object '{self.key}' already " - f"assigned to Table '{existing.description}'" - ) - - extra_remove = None - existing_col = None - conflicts_on = "" - - if self.key in table._columns: - existing_col = table._columns[self.key] - if self.key == self.name: - conflicts_on = "name" - else: - conflicts_on = "key" - elif self.name in all_names: - existing_col = all_names[self.name] - extra_remove = {existing_col} - conflicts_on = "name" - - if existing_col is not None: - if existing_col is not self: - if not allow_replacements: - raise exc.DuplicateColumnError( - f"A column with {conflicts_on} " - f"""'{ - self.key if conflicts_on == 'key' else self.name - }' """ - f"is already present in table '{table.name}'." - ) - for fk in existing_col.foreign_keys: - table.foreign_keys.remove(fk) - if fk.constraint in table.constraints: - # this might have been removed - # already, if it's a composite constraint - # and more than one col being replaced - table.constraints.remove(fk.constraint) - - if extra_remove and existing_col is not None and self.key == self.name: - util.warn( - f'Column with user-specified key "{existing_col.key}" is ' - "being replaced with " - f'plain named column "{self.name}", ' - f'key "{existing_col.key}" is being removed. If this is a ' - "reflection operation, specify autoload_replace=False to " - "prevent this replacement." - ) - table._columns.replace(self, extra_remove=extra_remove) - all_names[self.name] = self - self.table = table - - if self._insert_sentinel: - if self.table._sentinel_column is not None: - raise exc.ArgumentError( - "a Table may have only one explicit sentinel column" - ) - self.table._sentinel_column = self - - if self.primary_key: - table.primary_key._replace(self) - elif self.key in table.primary_key: - raise exc.ArgumentError( - f"Trying to redefine primary-key column '{self.key}' as a " - f"non-primary-key column on table '{table.fullname}'" - ) - - if self.index: - if isinstance(self.index, str): - raise exc.ArgumentError( - "The 'index' keyword argument on Column is boolean only. " - "To create indexes with a specific name, create an " - "explicit Index object external to the Table." - ) - table.append_constraint( - Index( - None, self.key, unique=bool(self.unique), _column_flag=True - ) - ) - - elif self.unique: - if isinstance(self.unique, str): - raise exc.ArgumentError( - "The 'unique' keyword argument on Column is boolean " - "only. To create unique constraints or indexes with a " - "specific name, append an explicit UniqueConstraint to " - "the Table's list of elements, or create an explicit " - "Index object external to the Table." - ) - table.append_constraint( - UniqueConstraint(self.key, _column_flag=True) - ) - - self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table)) - - if self.identity and ( - isinstance(self.default, Sequence) - or isinstance(self.onupdate, Sequence) - ): - raise exc.ArgumentError( - "An column cannot specify both Identity and Sequence." - ) - - def _setup_on_memoized_fks(self, fn: Callable[..., Any]) -> None: - fk_keys = [ - ((self.table.key, self.key), False), - ((self.table.key, self.name), True), - ] - for fk_key, link_to_name in fk_keys: - if fk_key in self.table.metadata._fk_memos: - for fk in self.table.metadata._fk_memos[fk_key]: - if fk.link_to_name is link_to_name: - fn(fk) - - def _on_table_attach(self, fn: Callable[..., Any]) -> None: - if self.table is not None: - fn(self, self.table) - else: - event.listen(self, "after_parent_attach", fn) - - @util.deprecated( - "1.4", - "The :meth:`_schema.Column.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy(self, **kw: Any) -> Column[Any]: - return self._copy(**kw) - - def _copy(self, **kw: Any) -> Column[Any]: - """Create a copy of this ``Column``, uninitialized. - - This is used in :meth:`_schema.Table.to_metadata` and by the ORM. - - """ - - # Constraint objects plus non-constraint-bound ForeignKey objects - args: List[SchemaItem] = [ - c._copy(**kw) for c in self.constraints if not c._type_bound - ] + [c._copy(**kw) for c in self.foreign_keys if not c.constraint] - - # ticket #5276 - column_kwargs = {} - for dialect_name in self.dialect_options: - dialect_options = self.dialect_options[dialect_name]._non_defaults - for ( - dialect_option_key, - dialect_option_value, - ) in dialect_options.items(): - column_kwargs[dialect_name + "_" + dialect_option_key] = ( - dialect_option_value - ) - - server_default = self.server_default - server_onupdate = self.server_onupdate - if isinstance(server_default, (Computed, Identity)): - # TODO: likely should be copied in all cases - args.append(server_default._copy(**kw)) - server_default = server_onupdate = None - - type_ = self.type - if isinstance(type_, SchemaEventTarget): - type_ = type_.copy(**kw) - - # TODO: DefaultGenerator is not copied here! it's just used again - # with _set_parent() pointing to the old column. see the new - # use of _copy() in the new _merge() method - - c = self._constructor( - name=self.name, - type_=type_, - key=self.key, - primary_key=self.primary_key, - unique=self.unique, - system=self.system, - # quote=self.quote, # disabled 2013-08-27 (commit 031ef080) - index=self.index, - autoincrement=self.autoincrement, - default=self.default, - server_default=server_default, - onupdate=self.onupdate, - server_onupdate=server_onupdate, - doc=self.doc, - comment=self.comment, - _omit_from_statements=self._omit_from_statements, - insert_sentinel=self._insert_sentinel, - *args, - **column_kwargs, - ) - - # copy the state of "nullable" exactly, to accommodate for - # ORM flipping the .nullable flag directly - c.nullable = self.nullable - c._user_defined_nullable = self._user_defined_nullable - - return self._schema_item_copy(c) - - def _merge(self, other: Column[Any]) -> None: - """merge the elements of another column into this one. - - this is used by ORM pep-593 merge and will likely need a lot - of fixes. - - - """ - - if self.primary_key: - other.primary_key = True - - if self.autoincrement != "auto" and other.autoincrement == "auto": - other.autoincrement = self.autoincrement - - if self.system: - other.system = self.system - - if self.info: - other.info.update(self.info) - - type_ = self.type - if not type_._isnull and other.type._isnull: - if isinstance(type_, SchemaEventTarget): - type_ = type_.copy() - - other.type = type_ - - if isinstance(type_, SchemaEventTarget): - type_._set_parent_with_dispatch(other) - - for impl in type_._variant_mapping.values(): - if isinstance(impl, SchemaEventTarget): - impl._set_parent_with_dispatch(other) - - if ( - self._user_defined_nullable is not NULL_UNSPECIFIED - and other._user_defined_nullable is NULL_UNSPECIFIED - ): - other.nullable = self.nullable - other._user_defined_nullable = self._user_defined_nullable - - if self.default is not None and other.default is None: - new_default = self.default._copy() - new_default._set_parent(other) - - if self.server_default and other.server_default is None: - new_server_default = self.server_default - if isinstance(new_server_default, FetchedValue): - new_server_default = new_server_default._copy() - new_server_default._set_parent(other) - else: - other.server_default = new_server_default - - if self.server_onupdate and other.server_onupdate is None: - new_server_onupdate = self.server_onupdate - new_server_onupdate = new_server_onupdate._copy() - new_server_onupdate._set_parent(other) - - if self.onupdate and other.onupdate is None: - new_onupdate = self.onupdate._copy() - new_onupdate._set_parent(other) - - if self.index in (True, False) and other.index is None: - other.index = self.index - - if self.unique in (True, False) and other.unique is None: - other.unique = self.unique - - if self.doc and other.doc is None: - other.doc = self.doc - - if self.comment and other.comment is None: - other.comment = self.comment - - for const in self.constraints: - if not const._type_bound: - new_const = const._copy() - new_const._set_parent(other) - - for fk in self.foreign_keys: - if not fk.constraint: - new_fk = fk._copy() - new_fk._set_parent(other) - - def _make_proxy( - self, - selectable: FromClause, - name: Optional[str] = None, - key: Optional[str] = None, - name_is_truncatable: bool = False, - compound_select_cols: Optional[ - _typing_Sequence[ColumnElement[Any]] - ] = None, - **kw: Any, - ) -> Tuple[str, ColumnClause[_T]]: - """Create a *proxy* for this column. - - This is a copy of this ``Column`` referenced by a different parent - (such as an alias or select statement). The column should - be used only in select scenarios, as its full DDL/default - information is not transferred. - - """ - - fk = [ - ForeignKey( - col if col is not None else f._colspec, - _unresolvable=col is None, - _constraint=f.constraint, - ) - for f, col in [ - (fk, fk._resolve_column(raiseerr=False)) - for fk in self.foreign_keys - ] - ] - - if name is None and self.name is None: - raise exc.InvalidRequestError( - "Cannot initialize a sub-selectable" - " with this Column object until its 'name' has " - "been assigned." - ) - try: - c = self._constructor( - ( - coercions.expect( - roles.TruncatedLabelRole, name if name else self.name - ) - if name_is_truncatable - else (name or self.name) - ), - self.type, - # this may actually be ._proxy_key when the key is incoming - key=key if key else name if name else self.key, - primary_key=self.primary_key, - nullable=self.nullable, - _proxies=( - list(compound_select_cols) - if compound_select_cols - else [self] - ), - *fk, - ) - except TypeError as err: - raise TypeError( - "Could not create a copy of this %r object. " - "Ensure the class includes a _constructor() " - "attribute or method which accepts the " - "standard Column constructor arguments, or " - "references the Column class itself." % self.__class__ - ) from err - - c.table = selectable - c._propagate_attrs = selectable._propagate_attrs - if selectable._is_clone_of is not None: - c._is_clone_of = selectable._is_clone_of.columns.get(c.key) - if self.primary_key: - selectable.primary_key.add(c) # type: ignore - if fk: - selectable.foreign_keys.update(fk) # type: ignore - return c.key, c - - -def insert_sentinel( - name: Optional[str] = None, - type_: Optional[_TypeEngineArgument[_T]] = None, - *, - default: Optional[Any] = None, - omit_from_statements: bool = True, -) -> Column[Any]: - """Provides a surrogate :class:`_schema.Column` that will act as a - dedicated insert :term:`sentinel` column, allowing efficient bulk - inserts with deterministic RETURNING sorting for tables that - don't otherwise have qualifying primary key configurations. - - Adding this column to a :class:`.Table` object requires that a - corresponding database table actually has this column present, so if adding - it to an existing model, existing database tables would need to be migrated - (e.g. using ALTER TABLE or similar) to include this column. - - For background on how this object is used, see the section - :ref:`engine_insertmanyvalues_sentinel_columns` as part of the - section :ref:`engine_insertmanyvalues`. - - The :class:`_schema.Column` returned will be a nullable integer column by - default and make use of a sentinel-specific default generator used only in - "insertmanyvalues" operations. - - .. seealso:: - - :func:`_orm.orm_insert_sentinel` - - :paramref:`_schema.Column.insert_sentinel` - - :ref:`engine_insertmanyvalues` - - :ref:`engine_insertmanyvalues_sentinel_columns` - - - .. versionadded:: 2.0.10 - - """ - return Column( - name=name, - type_=type_api.INTEGERTYPE if type_ is None else type_, - default=( - default if default is not None else _InsertSentinelColumnDefault() - ), - _omit_from_statements=omit_from_statements, - insert_sentinel=True, - ) - - -class ForeignKey(DialectKWArgs, SchemaItem): - """Defines a dependency between two columns. - - ``ForeignKey`` is specified as an argument to a :class:`_schema.Column` - object, - e.g.:: - - t = Table("remote_table", metadata, - Column("remote_id", ForeignKey("main_table.id")) - ) - - Note that ``ForeignKey`` is only a marker object that defines - a dependency between two columns. The actual constraint - is in all cases represented by the :class:`_schema.ForeignKeyConstraint` - object. This object will be generated automatically when - a ``ForeignKey`` is associated with a :class:`_schema.Column` which - in turn is associated with a :class:`_schema.Table`. Conversely, - when :class:`_schema.ForeignKeyConstraint` is applied to a - :class:`_schema.Table`, - ``ForeignKey`` markers are automatically generated to be - present on each associated :class:`_schema.Column`, which are also - associated with the constraint object. - - Note that you cannot define a "composite" foreign key constraint, - that is a constraint between a grouping of multiple parent/child - columns, using ``ForeignKey`` objects. To define this grouping, - the :class:`_schema.ForeignKeyConstraint` object must be used, and applied - to the :class:`_schema.Table`. The associated ``ForeignKey`` objects - are created automatically. - - The ``ForeignKey`` objects associated with an individual - :class:`_schema.Column` - object are available in the `foreign_keys` collection - of that column. - - Further examples of foreign key configuration are in - :ref:`metadata_foreignkeys`. - - """ - - __visit_name__ = "foreign_key" - - parent: Column[Any] - - _table_column: Optional[Column[Any]] - - def __init__( - self, - column: _DDLColumnArgument, - _constraint: Optional[ForeignKeyConstraint] = None, - use_alter: bool = False, - name: _ConstraintNameArgument = None, - onupdate: Optional[str] = None, - ondelete: Optional[str] = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - link_to_name: bool = False, - match: Optional[str] = None, - info: Optional[_InfoType] = None, - comment: Optional[str] = None, - _unresolvable: bool = False, - **dialect_kw: Any, - ): - r""" - Construct a column-level FOREIGN KEY. - - The :class:`_schema.ForeignKey` object when constructed generates a - :class:`_schema.ForeignKeyConstraint` - which is associated with the parent - :class:`_schema.Table` object's collection of constraints. - - :param column: A single target column for the key relationship. A - :class:`_schema.Column` object or a column name as a string: - ``tablename.columnkey`` or ``schema.tablename.columnkey``. - ``columnkey`` is the ``key`` which has been assigned to the column - (defaults to the column name itself), unless ``link_to_name`` is - ``True`` in which case the rendered name of the column is used. - - :param name: Optional string. An in-database name for the key if - `constraint` is not provided. - - :param onupdate: Optional string. If set, emit ON UPDATE <value> when - issuing DDL for this constraint. Typical values include CASCADE, - DELETE and RESTRICT. - - :param ondelete: Optional string. If set, emit ON DELETE <value> when - issuing DDL for this constraint. Typical values include CASCADE, - DELETE and RESTRICT. - - :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT - DEFERRABLE when issuing DDL for this constraint. - - :param initially: Optional string. If set, emit INITIALLY <value> when - issuing DDL for this constraint. - - :param link_to_name: if True, the string name given in ``column`` is - the rendered name of the referenced column, not its locally - assigned ``key``. - - :param use_alter: passed to the underlying - :class:`_schema.ForeignKeyConstraint` - to indicate the constraint should - be generated/dropped externally from the CREATE TABLE/ DROP TABLE - statement. See :paramref:`_schema.ForeignKeyConstraint.use_alter` - for further description. - - .. seealso:: - - :paramref:`_schema.ForeignKeyConstraint.use_alter` - - :ref:`use_alter` - - :param match: Optional string. If set, emit MATCH <value> when issuing - DDL for this constraint. Typical values include SIMPLE, PARTIAL - and FULL. - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param comment: Optional string that will render an SQL comment on - foreign key constraint creation. - - .. versionadded:: 2.0 - - :param \**dialect_kw: Additional keyword arguments are dialect - specific, and passed in the form ``<dialectname>_<argname>``. The - arguments are ultimately handled by a corresponding - :class:`_schema.ForeignKeyConstraint`. - See the documentation regarding - an individual dialect at :ref:`dialect_toplevel` for detail on - documented arguments. - - """ - - self._colspec = coercions.expect(roles.DDLReferredColumnRole, column) - self._unresolvable = _unresolvable - - if isinstance(self._colspec, str): - self._table_column = None - else: - self._table_column = self._colspec - - if not isinstance( - self._table_column.table, (type(None), TableClause) - ): - raise exc.ArgumentError( - "ForeignKey received Column not bound " - "to a Table, got: %r" % self._table_column.table - ) - - # the linked ForeignKeyConstraint. - # ForeignKey will create this when parent Column - # is attached to a Table, *or* ForeignKeyConstraint - # object passes itself in when creating ForeignKey - # markers. - self.constraint = _constraint - - # .parent is not Optional under normal use - self.parent = None # type: ignore - - self.use_alter = use_alter - self.name = name - self.onupdate = onupdate - self.ondelete = ondelete - self.deferrable = deferrable - self.initially = initially - self.link_to_name = link_to_name - self.match = match - self.comment = comment - if info: - self.info = info - self._unvalidated_dialect_kw = dialect_kw - - def __repr__(self) -> str: - return "ForeignKey(%r)" % self._get_colspec() - - @util.deprecated( - "1.4", - "The :meth:`_schema.ForeignKey.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey: - return self._copy(schema=schema, **kw) - - def _copy(self, *, schema: Optional[str] = None, **kw: Any) -> ForeignKey: - """Produce a copy of this :class:`_schema.ForeignKey` object. - - The new :class:`_schema.ForeignKey` will not be bound - to any :class:`_schema.Column`. - - This method is usually used by the internal - copy procedures of :class:`_schema.Column`, :class:`_schema.Table`, - and :class:`_schema.MetaData`. - - :param schema: The returned :class:`_schema.ForeignKey` will - reference the original table and column name, qualified - by the given string schema name. - - """ - fk = ForeignKey( - self._get_colspec(schema=schema), - use_alter=self.use_alter, - name=self.name, - onupdate=self.onupdate, - ondelete=self.ondelete, - deferrable=self.deferrable, - initially=self.initially, - link_to_name=self.link_to_name, - match=self.match, - comment=self.comment, - **self._unvalidated_dialect_kw, - ) - return self._schema_item_copy(fk) - - def _get_colspec( - self, - schema: Optional[ - Union[ - str, - Literal[SchemaConst.RETAIN_SCHEMA, SchemaConst.BLANK_SCHEMA], - ] - ] = None, - table_name: Optional[str] = None, - _is_copy: bool = False, - ) -> str: - """Return a string based 'column specification' for this - :class:`_schema.ForeignKey`. - - This is usually the equivalent of the string-based "tablename.colname" - argument first passed to the object's constructor. - - """ - if schema not in (None, RETAIN_SCHEMA): - _schema, tname, colname = self._column_tokens - if table_name is not None: - tname = table_name - if schema is BLANK_SCHEMA: - return "%s.%s" % (tname, colname) - else: - return "%s.%s.%s" % (schema, tname, colname) - elif table_name: - schema, tname, colname = self._column_tokens - if schema: - return "%s.%s.%s" % (schema, table_name, colname) - else: - return "%s.%s" % (table_name, colname) - elif self._table_column is not None: - if self._table_column.table is None: - if _is_copy: - raise exc.InvalidRequestError( - f"Can't copy ForeignKey object which refers to " - f"non-table bound Column {self._table_column!r}" - ) - else: - return self._table_column.key - return "%s.%s" % ( - self._table_column.table.fullname, - self._table_column.key, - ) - else: - assert isinstance(self._colspec, str) - return self._colspec - - @property - def _referred_schema(self) -> Optional[str]: - return self._column_tokens[0] - - def _table_key(self) -> Any: - if self._table_column is not None: - if self._table_column.table is None: - return None - else: - return self._table_column.table.key - else: - schema, tname, colname = self._column_tokens - return _get_table_key(tname, schema) - - target_fullname = property(_get_colspec) - - def references(self, table: Table) -> bool: - """Return True if the given :class:`_schema.Table` - is referenced by this - :class:`_schema.ForeignKey`.""" - - return table.corresponding_column(self.column) is not None - - def get_referent(self, table: FromClause) -> Optional[Column[Any]]: - """Return the :class:`_schema.Column` in the given - :class:`_schema.Table` (or any :class:`.FromClause`) - referenced by this :class:`_schema.ForeignKey`. - - Returns None if this :class:`_schema.ForeignKey` - does not reference the given - :class:`_schema.Table`. - - """ - # our column is a Column, and any subquery etc. proxying us - # would be doing so via another Column, so that's what would - # be returned here - return table.columns.corresponding_column(self.column) # type: ignore - - @util.memoized_property - def _column_tokens(self) -> Tuple[Optional[str], str, Optional[str]]: - """parse a string-based _colspec into its component parts.""" - - m = self._get_colspec().split(".") - if m is None: - raise exc.ArgumentError( - f"Invalid foreign key column specification: {self._colspec}" - ) - if len(m) == 1: - tname = m.pop() - colname = None - else: - colname = m.pop() - tname = m.pop() - - # A FK between column 'bar' and table 'foo' can be - # specified as 'foo', 'foo.bar', 'dbo.foo.bar', - # 'otherdb.dbo.foo.bar'. Once we have the column name and - # the table name, treat everything else as the schema - # name. Some databases (e.g. Sybase) support - # inter-database foreign keys. See tickets#1341 and -- - # indirectly related -- Ticket #594. This assumes that '.' - # will never appear *within* any component of the FK. - - if len(m) > 0: - schema = ".".join(m) - else: - schema = None - return schema, tname, colname - - def _resolve_col_tokens(self) -> Tuple[Table, str, Optional[str]]: - if self.parent is None: - raise exc.InvalidRequestError( - "this ForeignKey object does not yet have a " - "parent Column associated with it." - ) - - elif self.parent.table is None: - raise exc.InvalidRequestError( - "this ForeignKey's parent column is not yet associated " - "with a Table." - ) - - parenttable = self.parent.table - - if self._unresolvable: - schema, tname, colname = self._column_tokens - tablekey = _get_table_key(tname, schema) - return parenttable, tablekey, colname - - # assertion - # basically Column._make_proxy() sends the actual - # target Column to the ForeignKey object, so the - # string resolution here is never called. - for c in self.parent.base_columns: - if isinstance(c, Column): - assert c.table is parenttable - break - else: - assert False - ###################### - - schema, tname, colname = self._column_tokens - - if schema is None and parenttable.metadata.schema is not None: - schema = parenttable.metadata.schema - - tablekey = _get_table_key(tname, schema) - return parenttable, tablekey, colname - - def _link_to_col_by_colstring( - self, parenttable: Table, table: Table, colname: Optional[str] - ) -> Column[Any]: - _column = None - if colname is None: - # colname is None in the case that ForeignKey argument - # was specified as table name only, in which case we - # match the column name to the same column on the - # parent. - # this use case wasn't working in later 1.x series - # as it had no test coverage; fixed in 2.0 - parent = self.parent - assert parent is not None - key = parent.key - _column = table.c.get(key, None) - elif self.link_to_name: - key = colname - for c in table.c: - if c.name == colname: - _column = c - else: - key = colname - _column = table.c.get(colname, None) - - if _column is None: - raise exc.NoReferencedColumnError( - "Could not initialize target column " - f"for ForeignKey '{self._colspec}' " - f"on table '{parenttable.name}': " - f"table '{table.name}' has no column named '{key}'", - table.name, - key, - ) - - return _column - - def _set_target_column(self, column: Column[Any]) -> None: - assert self.parent is not None - - # propagate TypeEngine to parent if it didn't have one - if self.parent.type._isnull: - self.parent.type = column.type - - # super-edgy case, if other FKs point to our column, - # they'd get the type propagated out also. - - def set_type(fk: ForeignKey) -> None: - if fk.parent.type._isnull: - fk.parent.type = column.type - - self.parent._setup_on_memoized_fks(set_type) - - self.column = column # type: ignore - - @util.ro_memoized_property - def column(self) -> Column[Any]: - """Return the target :class:`_schema.Column` referenced by this - :class:`_schema.ForeignKey`. - - If no target column has been established, an exception - is raised. - - """ - - return self._resolve_column() - - @overload - def _resolve_column( - self, *, raiseerr: Literal[True] = ... - ) -> Column[Any]: ... - - @overload - def _resolve_column( - self, *, raiseerr: bool = ... - ) -> Optional[Column[Any]]: ... - - def _resolve_column( - self, *, raiseerr: bool = True - ) -> Optional[Column[Any]]: - _column: Column[Any] - - if isinstance(self._colspec, str): - parenttable, tablekey, colname = self._resolve_col_tokens() - - if self._unresolvable or tablekey not in parenttable.metadata: - if not raiseerr: - return None - raise exc.NoReferencedTableError( - f"Foreign key associated with column " - f"'{self.parent}' could not find " - f"table '{tablekey}' with which to generate a " - f"foreign key to target column '{colname}'", - tablekey, - ) - elif parenttable.key not in parenttable.metadata: - if not raiseerr: - return None - raise exc.InvalidRequestError( - f"Table {parenttable} is no longer associated with its " - "parent MetaData" - ) - else: - table = parenttable.metadata.tables[tablekey] - return self._link_to_col_by_colstring( - parenttable, table, colname - ) - - elif hasattr(self._colspec, "__clause_element__"): - _column = self._colspec.__clause_element__() - return _column - else: - _column = self._colspec - return _column - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, Column) - - if self.parent is not None and self.parent is not parent: - raise exc.InvalidRequestError( - "This ForeignKey already has a parent !" - ) - self.parent = parent - self.parent.foreign_keys.add(self) - self.parent._on_table_attach(self._set_table) - - def _set_remote_table(self, table: Table) -> None: - parenttable, _, colname = self._resolve_col_tokens() - _column = self._link_to_col_by_colstring(parenttable, table, colname) - self._set_target_column(_column) - assert self.constraint is not None - self.constraint._validate_dest_table(table) - - def _remove_from_metadata(self, metadata: MetaData) -> None: - parenttable, table_key, colname = self._resolve_col_tokens() - fk_key = (table_key, colname) - - if self in metadata._fk_memos[fk_key]: - # TODO: no test coverage for self not in memos - metadata._fk_memos[fk_key].remove(self) - - def _set_table(self, column: Column[Any], table: Table) -> None: - # standalone ForeignKey - create ForeignKeyConstraint - # on the hosting Table when attached to the Table. - assert isinstance(table, Table) - if self.constraint is None: - self.constraint = ForeignKeyConstraint( - [], - [], - use_alter=self.use_alter, - name=self.name, - onupdate=self.onupdate, - ondelete=self.ondelete, - deferrable=self.deferrable, - initially=self.initially, - match=self.match, - comment=self.comment, - **self._unvalidated_dialect_kw, - ) - self.constraint._append_element(column, self) - self.constraint._set_parent_with_dispatch(table) - table.foreign_keys.add(self) - # set up remote ".column" attribute, or a note to pick it - # up when the other Table/Column shows up - if isinstance(self._colspec, str): - parenttable, table_key, colname = self._resolve_col_tokens() - fk_key = (table_key, colname) - if table_key in parenttable.metadata.tables: - table = parenttable.metadata.tables[table_key] - try: - _column = self._link_to_col_by_colstring( - parenttable, table, colname - ) - except exc.NoReferencedColumnError: - # this is OK, we'll try later - pass - else: - self._set_target_column(_column) - - parenttable.metadata._fk_memos[fk_key].append(self) - elif hasattr(self._colspec, "__clause_element__"): - _column = self._colspec.__clause_element__() - self._set_target_column(_column) - else: - _column = self._colspec - self._set_target_column(_column) - - -if TYPE_CHECKING: - - def default_is_sequence( - obj: Optional[DefaultGenerator], - ) -> TypeGuard[Sequence]: ... - - def default_is_clause_element( - obj: Optional[DefaultGenerator], - ) -> TypeGuard[ColumnElementColumnDefault]: ... - - def default_is_scalar( - obj: Optional[DefaultGenerator], - ) -> TypeGuard[ScalarElementColumnDefault]: ... - -else: - default_is_sequence = operator.attrgetter("is_sequence") - - default_is_clause_element = operator.attrgetter("is_clause_element") - - default_is_scalar = operator.attrgetter("is_scalar") - - -class DefaultGenerator(Executable, SchemaItem): - """Base class for column *default* values. - - This object is only present on column.default or column.onupdate. - It's not valid as a server default. - - """ - - __visit_name__ = "default_generator" - - _is_default_generator = True - is_sequence = False - is_identity = False - is_server_default = False - is_clause_element = False - is_callable = False - is_scalar = False - has_arg = False - is_sentinel = False - column: Optional[Column[Any]] - - def __init__(self, for_update: bool = False) -> None: - self.for_update = for_update - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - if TYPE_CHECKING: - assert isinstance(parent, Column) - self.column = parent - if self.for_update: - self.column.onupdate = self - else: - self.column.default = self - - def _copy(self) -> DefaultGenerator: - raise NotImplementedError() - - def _execute_on_connection( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> Any: - util.warn_deprecated( - "Using the .execute() method to invoke a " - "DefaultGenerator object is deprecated; please use " - "the .scalar() method.", - "2.0", - ) - return self._execute_on_scalar( - connection, distilled_params, execution_options - ) - - def _execute_on_scalar( - self, - connection: Connection, - distilled_params: _CoreMultiExecuteParams, - execution_options: CoreExecuteOptionsParameter, - ) -> Any: - return connection._execute_default( - self, distilled_params, execution_options - ) - - -class ColumnDefault(DefaultGenerator, ABC): - """A plain default value on a column. - - This could correspond to a constant, a callable function, - or a SQL clause. - - :class:`.ColumnDefault` is generated automatically - whenever the ``default``, ``onupdate`` arguments of - :class:`_schema.Column` are used. A :class:`.ColumnDefault` - can be passed positionally as well. - - For example, the following:: - - Column('foo', Integer, default=50) - - Is equivalent to:: - - Column('foo', Integer, ColumnDefault(50)) - - - """ - - arg: Any - - @overload - def __new__( - cls, arg: Callable[..., Any], for_update: bool = ... - ) -> CallableColumnDefault: ... - - @overload - def __new__( - cls, arg: ColumnElement[Any], for_update: bool = ... - ) -> ColumnElementColumnDefault: ... - - # if I return ScalarElementColumnDefault here, which is what's actually - # returned, mypy complains that - # overloads overlap w/ incompatible return types. - @overload - def __new__(cls, arg: object, for_update: bool = ...) -> ColumnDefault: ... - - def __new__( - cls, arg: Any = None, for_update: bool = False - ) -> ColumnDefault: - """Construct a new :class:`.ColumnDefault`. - - - :param arg: argument representing the default value. - May be one of the following: - - * a plain non-callable Python value, such as a - string, integer, boolean, or other simple type. - The default value will be used as is each time. - * a SQL expression, that is one which derives from - :class:`_expression.ColumnElement`. The SQL expression will - be rendered into the INSERT or UPDATE statement, - or in the case of a primary key column when - RETURNING is not used may be - pre-executed before an INSERT within a SELECT. - * A Python callable. The function will be invoked for each - new row subject to an INSERT or UPDATE. - The callable must accept exactly - zero or one positional arguments. The one-argument form - will receive an instance of the :class:`.ExecutionContext`, - which provides contextual information as to the current - :class:`_engine.Connection` in use as well as the current - statement and parameters. - - """ - - if isinstance(arg, FetchedValue): - raise exc.ArgumentError( - "ColumnDefault may not be a server-side default type." - ) - elif callable(arg): - cls = CallableColumnDefault - elif isinstance(arg, ClauseElement): - cls = ColumnElementColumnDefault - elif arg is not None: - cls = ScalarElementColumnDefault - - return object.__new__(cls) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.arg!r})" - - -class ScalarElementColumnDefault(ColumnDefault): - """default generator for a fixed scalar Python value - - .. versionadded: 2.0 - - """ - - is_scalar = True - has_arg = True - - def __init__(self, arg: Any, for_update: bool = False) -> None: - self.for_update = for_update - self.arg = arg - - def _copy(self) -> ScalarElementColumnDefault: - return ScalarElementColumnDefault( - arg=self.arg, for_update=self.for_update - ) - - -class _InsertSentinelColumnDefault(ColumnDefault): - """Default generator that's specific to the use of a "sentinel" column - when using the insertmanyvalues feature. - - This default is used as part of the :func:`_schema.insert_sentinel` - construct. - - """ - - is_sentinel = True - for_update = False - arg = None - - def __new__(cls) -> _InsertSentinelColumnDefault: - return object.__new__(cls) - - def __init__(self) -> None: - pass - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - col = cast("Column[Any]", parent) - if not col._insert_sentinel: - raise exc.ArgumentError( - "The _InsertSentinelColumnDefault may only be applied to a " - "Column marked as insert_sentinel=True" - ) - elif not col.nullable: - raise exc.ArgumentError( - "The _InsertSentinelColumnDefault may only be applied to a " - "Column that is nullable" - ) - - super()._set_parent(parent, **kw) - - def _copy(self) -> _InsertSentinelColumnDefault: - return _InsertSentinelColumnDefault() - - -_SQLExprDefault = Union["ColumnElement[Any]", "TextClause"] - - -class ColumnElementColumnDefault(ColumnDefault): - """default generator for a SQL expression - - .. versionadded:: 2.0 - - """ - - is_clause_element = True - has_arg = True - arg: _SQLExprDefault - - def __init__( - self, - arg: _SQLExprDefault, - for_update: bool = False, - ) -> None: - self.for_update = for_update - self.arg = arg - - def _copy(self) -> ColumnElementColumnDefault: - return ColumnElementColumnDefault( - arg=self.arg, for_update=self.for_update - ) - - @util.memoized_property - @util.preload_module("sqlalchemy.sql.sqltypes") - def _arg_is_typed(self) -> bool: - sqltypes = util.preloaded.sql_sqltypes - - return not isinstance(self.arg.type, sqltypes.NullType) - - -class _CallableColumnDefaultProtocol(Protocol): - def __call__(self, context: ExecutionContext) -> Any: ... - - -class CallableColumnDefault(ColumnDefault): - """default generator for a callable Python function - - .. versionadded:: 2.0 - - """ - - is_callable = True - arg: _CallableColumnDefaultProtocol - has_arg = True - - def __init__( - self, - arg: Union[_CallableColumnDefaultProtocol, Callable[[], Any]], - for_update: bool = False, - ) -> None: - self.for_update = for_update - self.arg = self._maybe_wrap_callable(arg) - - def _copy(self) -> CallableColumnDefault: - return CallableColumnDefault(arg=self.arg, for_update=self.for_update) - - def _maybe_wrap_callable( - self, fn: Union[_CallableColumnDefaultProtocol, Callable[[], Any]] - ) -> _CallableColumnDefaultProtocol: - """Wrap callables that don't accept a context. - - This is to allow easy compatibility with default callables - that aren't specific to accepting of a context. - - """ - - try: - argspec = util.get_callable_argspec(fn, no_self=True) - except TypeError: - return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore - - defaulted = argspec[3] is not None and len(argspec[3]) or 0 - positionals = len(argspec[0]) - defaulted - - if positionals == 0: - return util.wrap_callable(lambda ctx: fn(), fn) # type: ignore - - elif positionals == 1: - return fn # type: ignore - else: - raise exc.ArgumentError( - "ColumnDefault Python function takes zero or one " - "positional arguments" - ) - - -class IdentityOptions: - """Defines options for a named database sequence or an identity column. - - .. versionadded:: 1.3.18 - - .. seealso:: - - :class:`.Sequence` - - """ - - def __init__( - self, - start: Optional[int] = None, - increment: Optional[int] = None, - minvalue: Optional[int] = None, - maxvalue: Optional[int] = None, - nominvalue: Optional[bool] = None, - nomaxvalue: Optional[bool] = None, - cycle: Optional[bool] = None, - cache: Optional[int] = None, - order: Optional[bool] = None, - ) -> None: - """Construct a :class:`.IdentityOptions` object. - - See the :class:`.Sequence` documentation for a complete description - of the parameters. - - :param start: the starting index of the sequence. - :param increment: the increment value of the sequence. - :param minvalue: the minimum value of the sequence. - :param maxvalue: the maximum value of the sequence. - :param nominvalue: no minimum value of the sequence. - :param nomaxvalue: no maximum value of the sequence. - :param cycle: allows the sequence to wrap around when the maxvalue - or minvalue has been reached. - :param cache: optional integer value; number of future values in the - sequence which are calculated in advance. - :param order: optional boolean value; if ``True``, renders the - ORDER keyword. - - """ - self.start = start - self.increment = increment - self.minvalue = minvalue - self.maxvalue = maxvalue - self.nominvalue = nominvalue - self.nomaxvalue = nomaxvalue - self.cycle = cycle - self.cache = cache - self.order = order - - @property - def _increment_is_negative(self) -> bool: - return self.increment is not None and self.increment < 0 - - -class Sequence(HasSchemaAttr, IdentityOptions, DefaultGenerator): - """Represents a named database sequence. - - The :class:`.Sequence` object represents the name and configurational - parameters of a database sequence. It also represents - a construct that can be "executed" by a SQLAlchemy :class:`_engine.Engine` - or :class:`_engine.Connection`, - rendering the appropriate "next value" function - for the target database and returning a result. - - The :class:`.Sequence` is typically associated with a primary key column:: - - some_table = Table( - 'some_table', metadata, - Column('id', Integer, Sequence('some_table_seq', start=1), - primary_key=True) - ) - - When CREATE TABLE is emitted for the above :class:`_schema.Table`, if the - target platform supports sequences, a CREATE SEQUENCE statement will - be emitted as well. For platforms that don't support sequences, - the :class:`.Sequence` construct is ignored. - - .. seealso:: - - :ref:`defaults_sequences` - - :class:`.CreateSequence` - - :class:`.DropSequence` - - """ - - __visit_name__ = "sequence" - - is_sequence = True - - column: Optional[Column[Any]] - data_type: Optional[TypeEngine[int]] - - def __init__( - self, - name: str, - start: Optional[int] = None, - increment: Optional[int] = None, - minvalue: Optional[int] = None, - maxvalue: Optional[int] = None, - nominvalue: Optional[bool] = None, - nomaxvalue: Optional[bool] = None, - cycle: Optional[bool] = None, - schema: Optional[Union[str, Literal[SchemaConst.BLANK_SCHEMA]]] = None, - cache: Optional[int] = None, - order: Optional[bool] = None, - data_type: Optional[_TypeEngineArgument[int]] = None, - optional: bool = False, - quote: Optional[bool] = None, - metadata: Optional[MetaData] = None, - quote_schema: Optional[bool] = None, - for_update: bool = False, - ) -> None: - """Construct a :class:`.Sequence` object. - - :param name: the name of the sequence. - - :param start: the starting index of the sequence. This value is - used when the CREATE SEQUENCE command is emitted to the database - as the value of the "START WITH" clause. If ``None``, the - clause is omitted, which on most platforms indicates a starting - value of 1. - - .. versionchanged:: 2.0 The :paramref:`.Sequence.start` parameter - is required in order to have DDL emit "START WITH". This is a - reversal of a change made in version 1.4 which would implicitly - render "START WITH 1" if the :paramref:`.Sequence.start` were - not included. See :ref:`change_7211` for more detail. - - :param increment: the increment value of the sequence. This - value is used when the CREATE SEQUENCE command is emitted to - the database as the value of the "INCREMENT BY" clause. If ``None``, - the clause is omitted, which on most platforms indicates an - increment of 1. - :param minvalue: the minimum value of the sequence. This - value is used when the CREATE SEQUENCE command is emitted to - the database as the value of the "MINVALUE" clause. If ``None``, - the clause is omitted, which on most platforms indicates a - minvalue of 1 and -2^63-1 for ascending and descending sequences, - respectively. - - :param maxvalue: the maximum value of the sequence. This - value is used when the CREATE SEQUENCE command is emitted to - the database as the value of the "MAXVALUE" clause. If ``None``, - the clause is omitted, which on most platforms indicates a - maxvalue of 2^63-1 and -1 for ascending and descending sequences, - respectively. - - :param nominvalue: no minimum value of the sequence. This - value is used when the CREATE SEQUENCE command is emitted to - the database as the value of the "NO MINVALUE" clause. If ``None``, - the clause is omitted, which on most platforms indicates a - minvalue of 1 and -2^63-1 for ascending and descending sequences, - respectively. - - :param nomaxvalue: no maximum value of the sequence. This - value is used when the CREATE SEQUENCE command is emitted to - the database as the value of the "NO MAXVALUE" clause. If ``None``, - the clause is omitted, which on most platforms indicates a - maxvalue of 2^63-1 and -1 for ascending and descending sequences, - respectively. - - :param cycle: allows the sequence to wrap around when the maxvalue - or minvalue has been reached by an ascending or descending sequence - respectively. This value is used when the CREATE SEQUENCE command - is emitted to the database as the "CYCLE" clause. If the limit is - reached, the next number generated will be the minvalue or maxvalue, - respectively. If cycle=False (the default) any calls to nextval - after the sequence has reached its maximum value will return an - error. - - :param schema: optional schema name for the sequence, if located - in a schema other than the default. The rules for selecting the - schema name when a :class:`_schema.MetaData` - is also present are the same - as that of :paramref:`_schema.Table.schema`. - - :param cache: optional integer value; number of future values in the - sequence which are calculated in advance. Renders the CACHE keyword - understood by Oracle and PostgreSQL. - - :param order: optional boolean value; if ``True``, renders the - ORDER keyword, understood by Oracle, indicating the sequence is - definitively ordered. May be necessary to provide deterministic - ordering using Oracle RAC. - - :param data_type: The type to be returned by the sequence, for - dialects that allow us to choose between INTEGER, BIGINT, etc. - (e.g., mssql). - - .. versionadded:: 1.4.0 - - :param optional: boolean value, when ``True``, indicates that this - :class:`.Sequence` object only needs to be explicitly generated - on backends that don't provide another way to generate primary - key identifiers. Currently, it essentially means, "don't create - this sequence on the PostgreSQL backend, where the SERIAL keyword - creates a sequence for us automatically". - :param quote: boolean value, when ``True`` or ``False``, explicitly - forces quoting of the :paramref:`_schema.Sequence.name` on or off. - When left at its default of ``None``, normal quoting rules based - on casing and reserved words take place. - :param quote_schema: Set the quoting preferences for the ``schema`` - name. - - :param metadata: optional :class:`_schema.MetaData` object which this - :class:`.Sequence` will be associated with. A :class:`.Sequence` - that is associated with a :class:`_schema.MetaData` - gains the following - capabilities: - - * The :class:`.Sequence` will inherit the - :paramref:`_schema.MetaData.schema` - parameter specified to the target :class:`_schema.MetaData`, which - affects the production of CREATE / DROP DDL, if any. - - * The :meth:`.Sequence.create` and :meth:`.Sequence.drop` methods - automatically use the engine bound to the :class:`_schema.MetaData` - object, if any. - - * The :meth:`_schema.MetaData.create_all` and - :meth:`_schema.MetaData.drop_all` - methods will emit CREATE / DROP for this :class:`.Sequence`, - even if the :class:`.Sequence` is not associated with any - :class:`_schema.Table` / :class:`_schema.Column` - that's a member of this - :class:`_schema.MetaData`. - - The above behaviors can only occur if the :class:`.Sequence` is - explicitly associated with the :class:`_schema.MetaData` - via this parameter. - - .. seealso:: - - :ref:`sequence_metadata` - full discussion of the - :paramref:`.Sequence.metadata` parameter. - - :param for_update: Indicates this :class:`.Sequence`, when associated - with a :class:`_schema.Column`, - should be invoked for UPDATE statements - on that column's table, rather than for INSERT statements, when - no value is otherwise present for that column in the statement. - - """ - DefaultGenerator.__init__(self, for_update=for_update) - IdentityOptions.__init__( - self, - start=start, - increment=increment, - minvalue=minvalue, - maxvalue=maxvalue, - nominvalue=nominvalue, - nomaxvalue=nomaxvalue, - cycle=cycle, - cache=cache, - order=order, - ) - self.column = None - self.name = quoted_name(name, quote) - self.optional = optional - if schema is BLANK_SCHEMA: - self.schema = schema = None - elif metadata is not None and schema is None and metadata.schema: - self.schema = schema = metadata.schema - else: - self.schema = quoted_name.construct(schema, quote_schema) - self.metadata = metadata - self._key = _get_table_key(name, schema) - if metadata: - self._set_metadata(metadata) - if data_type is not None: - self.data_type = to_instance(data_type) - else: - self.data_type = None - - @util.preload_module("sqlalchemy.sql.functions") - def next_value(self) -> Function[int]: - """Return a :class:`.next_value` function element - which will render the appropriate increment function - for this :class:`.Sequence` within any SQL expression. - - """ - return util.preloaded.sql_functions.func.next_value(self) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - column = parent - assert isinstance(column, Column) - super()._set_parent(column) - column._on_table_attach(self._set_table) - - def _copy(self) -> Sequence: - return Sequence( - name=self.name, - start=self.start, - increment=self.increment, - minvalue=self.minvalue, - maxvalue=self.maxvalue, - nominvalue=self.nominvalue, - nomaxvalue=self.nomaxvalue, - cycle=self.cycle, - schema=self.schema, - cache=self.cache, - order=self.order, - data_type=self.data_type, - optional=self.optional, - metadata=self.metadata, - for_update=self.for_update, - ) - - def _set_table(self, column: Column[Any], table: Table) -> None: - self._set_metadata(table.metadata) - - def _set_metadata(self, metadata: MetaData) -> None: - self.metadata = metadata - self.metadata._sequences[self._key] = self - - def create(self, bind: _CreateDropBind, checkfirst: bool = True) -> None: - """Creates this sequence in the database.""" - - bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst) - - def drop(self, bind: _CreateDropBind, checkfirst: bool = True) -> None: - """Drops this sequence from the database.""" - - bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst) - - def _not_a_column_expr(self) -> NoReturn: - raise exc.InvalidRequestError( - f"This {self.__class__.__name__} cannot be used directly " - "as a column expression. Use func.next_value(sequence) " - "to produce a 'next value' function that's usable " - "as a column element." - ) - - -@inspection._self_inspects -class FetchedValue(SchemaEventTarget): - """A marker for a transparent database-side default. - - Use :class:`.FetchedValue` when the database is configured - to provide some automatic default for a column. - - E.g.:: - - Column('foo', Integer, FetchedValue()) - - Would indicate that some trigger or default generator - will create a new value for the ``foo`` column during an - INSERT. - - .. seealso:: - - :ref:`triggered_columns` - - """ - - is_server_default = True - reflected = False - has_argument = False - is_clause_element = False - is_identity = False - - column: Optional[Column[Any]] - - def __init__(self, for_update: bool = False) -> None: - self.for_update = for_update - - def _as_for_update(self, for_update: bool) -> FetchedValue: - if for_update == self.for_update: - return self - else: - return self._clone(for_update) - - def _copy(self) -> FetchedValue: - return FetchedValue(self.for_update) - - def _clone(self, for_update: bool) -> Self: - n = self.__class__.__new__(self.__class__) - n.__dict__.update(self.__dict__) - n.__dict__.pop("column", None) - n.for_update = for_update - return n - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - column = parent - assert isinstance(column, Column) - self.column = column - if self.for_update: - self.column.server_onupdate = self - else: - self.column.server_default = self - - def __repr__(self) -> str: - return util.generic_repr(self) - - -class DefaultClause(FetchedValue): - """A DDL-specified DEFAULT column value. - - :class:`.DefaultClause` is a :class:`.FetchedValue` - that also generates a "DEFAULT" clause when - "CREATE TABLE" is emitted. - - :class:`.DefaultClause` is generated automatically - whenever the ``server_default``, ``server_onupdate`` arguments of - :class:`_schema.Column` are used. A :class:`.DefaultClause` - can be passed positionally as well. - - For example, the following:: - - Column('foo', Integer, server_default="50") - - Is equivalent to:: - - Column('foo', Integer, DefaultClause("50")) - - """ - - has_argument = True - - def __init__( - self, - arg: Union[str, ClauseElement, TextClause], - for_update: bool = False, - _reflected: bool = False, - ) -> None: - util.assert_arg_type(arg, (str, ClauseElement, TextClause), "arg") - super().__init__(for_update) - self.arg = arg - self.reflected = _reflected - - def _copy(self) -> DefaultClause: - return DefaultClause( - arg=self.arg, for_update=self.for_update, _reflected=self.reflected - ) - - def __repr__(self) -> str: - return "DefaultClause(%r, for_update=%r)" % (self.arg, self.for_update) - - -class Constraint(DialectKWArgs, HasConditionalDDL, SchemaItem): - """A table-level SQL constraint. - - :class:`_schema.Constraint` serves as the base class for the series of - constraint objects that can be associated with :class:`_schema.Table` - objects, including :class:`_schema.PrimaryKeyConstraint`, - :class:`_schema.ForeignKeyConstraint` - :class:`_schema.UniqueConstraint`, and - :class:`_schema.CheckConstraint`. - - """ - - __visit_name__ = "constraint" - - _creation_order: int - _column_flag: bool - - def __init__( - self, - name: _ConstraintNameArgument = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - info: Optional[_InfoType] = None, - comment: Optional[str] = None, - _create_rule: Optional[Any] = None, - _type_bound: bool = False, - **dialect_kw: Any, - ) -> None: - r"""Create a SQL constraint. - - :param name: - Optional, the in-database name of this ``Constraint``. - - :param deferrable: - Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when - issuing DDL for this constraint. - - :param initially: - Optional string. If set, emit INITIALLY <value> when issuing DDL - for this constraint. - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param comment: Optional string that will render an SQL comment on - foreign key constraint creation. - - .. versionadded:: 2.0 - - :param \**dialect_kw: Additional keyword arguments are dialect - specific, and passed in the form ``<dialectname>_<argname>``. See - the documentation regarding an individual dialect at - :ref:`dialect_toplevel` for detail on documented arguments. - - :param _create_rule: - used internally by some datatypes that also create constraints. - - :param _type_bound: - used internally to indicate that this constraint is associated with - a specific datatype. - - """ - - self.name = name - self.deferrable = deferrable - self.initially = initially - if info: - self.info = info - self._create_rule = _create_rule - self._type_bound = _type_bound - util.set_creation_order(self) - self._validate_dialect_kwargs(dialect_kw) - self.comment = comment - - def _should_create_for_compiler( - self, compiler: DDLCompiler, **kw: Any - ) -> bool: - if self._create_rule is not None and not self._create_rule(compiler): - return False - elif self._ddl_if is not None: - return self._ddl_if._should_execute( - ddl.CreateConstraint(self), self, None, compiler=compiler, **kw - ) - else: - return True - - @property - def table(self) -> Table: - try: - if isinstance(self.parent, Table): - return self.parent - except AttributeError: - pass - raise exc.InvalidRequestError( - "This constraint is not bound to a table. Did you " - "mean to call table.append_constraint(constraint) ?" - ) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, (Table, Column)) - self.parent = parent - parent.constraints.add(self) - - @util.deprecated( - "1.4", - "The :meth:`_schema.Constraint.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy(self, **kw: Any) -> Self: - return self._copy(**kw) - - def _copy(self, **kw: Any) -> Self: - raise NotImplementedError() - - -class ColumnCollectionMixin: - """A :class:`_expression.ColumnCollection` of :class:`_schema.Column` - objects. - - This collection represents the columns which are referred to by - this object. - - """ - - _columns: DedupeColumnCollection[Column[Any]] - - _allow_multiple_tables = False - - _pending_colargs: List[Optional[Union[str, Column[Any]]]] - - if TYPE_CHECKING: - - def _set_parent_with_dispatch( - self, parent: SchemaEventTarget, **kw: Any - ) -> None: ... - - def __init__( - self, - *columns: _DDLColumnArgument, - _autoattach: bool = True, - _column_flag: bool = False, - _gather_expressions: Optional[ - List[Union[str, ColumnElement[Any]]] - ] = None, - ) -> None: - self._column_flag = _column_flag - self._columns = DedupeColumnCollection() - - processed_expressions: Optional[ - List[Union[ColumnElement[Any], str]] - ] = _gather_expressions - - if processed_expressions is not None: - self._pending_colargs = [] - for ( - expr, - _, - _, - add_element, - ) in coercions.expect_col_expression_collection( - roles.DDLConstraintColumnRole, columns - ): - self._pending_colargs.append(add_element) - processed_expressions.append(expr) - else: - self._pending_colargs = [ - coercions.expect(roles.DDLConstraintColumnRole, column) - for column in columns - ] - - if _autoattach and self._pending_colargs: - self._check_attach() - - def _check_attach(self, evt: bool = False) -> None: - col_objs = [c for c in self._pending_colargs if isinstance(c, Column)] - - cols_w_table = [c for c in col_objs if isinstance(c.table, Table)] - - cols_wo_table = set(col_objs).difference(cols_w_table) - if cols_wo_table: - # feature #3341 - place event listeners for Column objects - # such that when all those cols are attached, we autoattach. - assert not evt, "Should not reach here on event call" - - # issue #3411 - don't do the per-column auto-attach if some of the - # columns are specified as strings. - has_string_cols = { - c for c in self._pending_colargs if c is not None - }.difference(col_objs) - if not has_string_cols: - - def _col_attached(column: Column[Any], table: Table) -> None: - # this isinstance() corresponds with the - # isinstance() above; only want to count Table-bound - # columns - if isinstance(table, Table): - cols_wo_table.discard(column) - if not cols_wo_table: - self._check_attach(evt=True) - - self._cols_wo_table = cols_wo_table - for col in cols_wo_table: - col._on_table_attach(_col_attached) - return - - columns = cols_w_table - - tables = {c.table for c in columns} - if len(tables) == 1: - self._set_parent_with_dispatch(tables.pop()) - elif len(tables) > 1 and not self._allow_multiple_tables: - table = columns[0].table - others = [c for c in columns[1:] if c.table is not table] - if others: - # black could not format this inline - other_str = ", ".join("'%s'" % c for c in others) - raise exc.ArgumentError( - f"Column(s) {other_str} " - f"are not part of table '{table.description}'." - ) - - @util.ro_memoized_property - def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]: - return self._columns.as_readonly() - - @util.ro_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]: - return self._columns.as_readonly() - - def _col_expressions( - self, parent: Union[Table, Column[Any]] - ) -> List[Optional[Column[Any]]]: - if isinstance(parent, Column): - result: List[Optional[Column[Any]]] = [ - c for c in self._pending_colargs if isinstance(c, Column) - ] - assert len(result) == len(self._pending_colargs) - return result - else: - try: - return [ - parent.c[col] if isinstance(col, str) else col - for col in self._pending_colargs - ] - except KeyError as ke: - raise exc.ConstraintColumnNotFoundError( - f"Can't create {self.__class__.__name__} " - f"on table '{parent.description}': no column " - f"named '{ke.args[0]}' is present." - ) from ke - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, (Table, Column)) - - for col in self._col_expressions(parent): - if col is not None: - self._columns.add(col) - - -class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint): - """A constraint that proxies a ColumnCollection.""" - - def __init__( - self, - *columns: _DDLColumnArgument, - name: _ConstraintNameArgument = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - info: Optional[_InfoType] = None, - _autoattach: bool = True, - _column_flag: bool = False, - _gather_expressions: Optional[List[_DDLColumnArgument]] = None, - **dialect_kw: Any, - ) -> None: - r""" - :param \*columns: - A sequence of column names or Column objects. - - :param name: - Optional, the in-database name of this constraint. - - :param deferrable: - Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when - issuing DDL for this constraint. - - :param initially: - Optional string. If set, emit INITIALLY <value> when issuing DDL - for this constraint. - - :param \**dialect_kw: other keyword arguments including - dialect-specific arguments are propagated to the :class:`.Constraint` - superclass. - - """ - Constraint.__init__( - self, - name=name, - deferrable=deferrable, - initially=initially, - info=info, - **dialect_kw, - ) - ColumnCollectionMixin.__init__( - self, *columns, _autoattach=_autoattach, _column_flag=_column_flag - ) - - columns: ReadOnlyColumnCollection[str, Column[Any]] - """A :class:`_expression.ColumnCollection` representing the set of columns - for this constraint. - - """ - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, (Column, Table)) - Constraint._set_parent(self, parent) - ColumnCollectionMixin._set_parent(self, parent) - - def __contains__(self, x: Any) -> bool: - return x in self._columns - - @util.deprecated( - "1.4", - "The :meth:`_schema.ColumnCollectionConstraint.copy` method " - "is deprecated and will be removed in a future release.", - ) - def copy( - self, - *, - target_table: Optional[Table] = None, - **kw: Any, - ) -> ColumnCollectionConstraint: - return self._copy(target_table=target_table, **kw) - - def _copy( - self, - *, - target_table: Optional[Table] = None, - **kw: Any, - ) -> ColumnCollectionConstraint: - # ticket #5276 - constraint_kwargs = {} - for dialect_name in self.dialect_options: - dialect_options = self.dialect_options[dialect_name]._non_defaults - for ( - dialect_option_key, - dialect_option_value, - ) in dialect_options.items(): - constraint_kwargs[dialect_name + "_" + dialect_option_key] = ( - dialect_option_value - ) - - assert isinstance(self.parent, Table) - c = self.__class__( - name=self.name, - deferrable=self.deferrable, - initially=self.initially, - *[ - _copy_expression(expr, self.parent, target_table) - for expr in self._columns - ], - comment=self.comment, - **constraint_kwargs, - ) - return self._schema_item_copy(c) - - def contains_column(self, col: Column[Any]) -> bool: - """Return True if this constraint contains the given column. - - Note that this object also contains an attribute ``.columns`` - which is a :class:`_expression.ColumnCollection` of - :class:`_schema.Column` objects. - - """ - - return self._columns.contains_column(col) - - def __iter__(self) -> Iterator[Column[Any]]: - return iter(self._columns) - - def __len__(self) -> int: - return len(self._columns) - - -class CheckConstraint(ColumnCollectionConstraint): - """A table- or column-level CHECK constraint. - - Can be included in the definition of a Table or Column. - """ - - _allow_multiple_tables = True - - __visit_name__ = "table_or_column_check_constraint" - - @_document_text_coercion( - "sqltext", - ":class:`.CheckConstraint`", - ":paramref:`.CheckConstraint.sqltext`", - ) - def __init__( - self, - sqltext: _TextCoercedExpressionArgument[Any], - name: _ConstraintNameArgument = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - table: Optional[Table] = None, - info: Optional[_InfoType] = None, - _create_rule: Optional[Any] = None, - _autoattach: bool = True, - _type_bound: bool = False, - **dialect_kw: Any, - ) -> None: - r"""Construct a CHECK constraint. - - :param sqltext: - A string containing the constraint definition, which will be used - verbatim, or a SQL expression construct. If given as a string, - the object is converted to a :func:`_expression.text` object. - If the textual - string includes a colon character, escape this using a backslash:: - - CheckConstraint(r"foo ~ E'a(?\:b|c)d") - - :param name: - Optional, the in-database name of the constraint. - - :param deferrable: - Optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when - issuing DDL for this constraint. - - :param initially: - Optional string. If set, emit INITIALLY <value> when issuing DDL - for this constraint. - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - """ - - self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext) - columns: List[Column[Any]] = [] - visitors.traverse(self.sqltext, {}, {"column": columns.append}) - - super().__init__( - name=name, - deferrable=deferrable, - initially=initially, - _create_rule=_create_rule, - info=info, - _type_bound=_type_bound, - _autoattach=_autoattach, - *columns, - **dialect_kw, - ) - if table is not None: - self._set_parent_with_dispatch(table) - - @property - def is_column_level(self) -> bool: - return not isinstance(self.parent, Table) - - @util.deprecated( - "1.4", - "The :meth:`_schema.CheckConstraint.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy( - self, *, target_table: Optional[Table] = None, **kw: Any - ) -> CheckConstraint: - return self._copy(target_table=target_table, **kw) - - def _copy( - self, *, target_table: Optional[Table] = None, **kw: Any - ) -> CheckConstraint: - if target_table is not None: - # note that target_table is None for the copy process of - # a column-bound CheckConstraint, so this path is not reached - # in that case. - sqltext = _copy_expression(self.sqltext, self.table, target_table) - else: - sqltext = self.sqltext - c = CheckConstraint( - sqltext, - name=self.name, - initially=self.initially, - deferrable=self.deferrable, - _create_rule=self._create_rule, - table=target_table, - comment=self.comment, - _autoattach=False, - _type_bound=self._type_bound, - ) - return self._schema_item_copy(c) - - -class ForeignKeyConstraint(ColumnCollectionConstraint): - """A table-level FOREIGN KEY constraint. - - Defines a single column or composite FOREIGN KEY ... REFERENCES - constraint. For a no-frills, single column foreign key, adding a - :class:`_schema.ForeignKey` to the definition of a :class:`_schema.Column` - is a - shorthand equivalent for an unnamed, single column - :class:`_schema.ForeignKeyConstraint`. - - Examples of foreign key configuration are in :ref:`metadata_foreignkeys`. - - """ - - __visit_name__ = "foreign_key_constraint" - - def __init__( - self, - columns: _typing_Sequence[_DDLColumnArgument], - refcolumns: _typing_Sequence[_DDLColumnArgument], - name: _ConstraintNameArgument = None, - onupdate: Optional[str] = None, - ondelete: Optional[str] = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - use_alter: bool = False, - link_to_name: bool = False, - match: Optional[str] = None, - table: Optional[Table] = None, - info: Optional[_InfoType] = None, - comment: Optional[str] = None, - **dialect_kw: Any, - ) -> None: - r"""Construct a composite-capable FOREIGN KEY. - - :param columns: A sequence of local column names. The named columns - must be defined and present in the parent Table. The names should - match the ``key`` given to each column (defaults to the name) unless - ``link_to_name`` is True. - - :param refcolumns: A sequence of foreign column names or Column - objects. The columns must all be located within the same Table. - - :param name: Optional, the in-database name of the key. - - :param onupdate: Optional string. If set, emit ON UPDATE <value> when - issuing DDL for this constraint. Typical values include CASCADE, - DELETE and RESTRICT. - - :param ondelete: Optional string. If set, emit ON DELETE <value> when - issuing DDL for this constraint. Typical values include CASCADE, - DELETE and RESTRICT. - - :param deferrable: Optional bool. If set, emit DEFERRABLE or NOT - DEFERRABLE when issuing DDL for this constraint. - - :param initially: Optional string. If set, emit INITIALLY <value> when - issuing DDL for this constraint. - - :param link_to_name: if True, the string name given in ``column`` is - the rendered name of the referenced column, not its locally assigned - ``key``. - - :param use_alter: If True, do not emit the DDL for this constraint as - part of the CREATE TABLE definition. Instead, generate it via an - ALTER TABLE statement issued after the full collection of tables - have been created, and drop it via an ALTER TABLE statement before - the full collection of tables are dropped. - - The use of :paramref:`_schema.ForeignKeyConstraint.use_alter` is - particularly geared towards the case where two or more tables - are established within a mutually-dependent foreign key constraint - relationship; however, the :meth:`_schema.MetaData.create_all` and - :meth:`_schema.MetaData.drop_all` - methods will perform this resolution - automatically, so the flag is normally not needed. - - .. seealso:: - - :ref:`use_alter` - - :param match: Optional string. If set, emit MATCH <value> when issuing - DDL for this constraint. Typical values include SIMPLE, PARTIAL - and FULL. - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param comment: Optional string that will render an SQL comment on - foreign key constraint creation. - - .. versionadded:: 2.0 - - :param \**dialect_kw: Additional keyword arguments are dialect - specific, and passed in the form ``<dialectname>_<argname>``. See - the documentation regarding an individual dialect at - :ref:`dialect_toplevel` for detail on documented arguments. - - """ - - Constraint.__init__( - self, - name=name, - deferrable=deferrable, - initially=initially, - info=info, - comment=comment, - **dialect_kw, - ) - self.onupdate = onupdate - self.ondelete = ondelete - self.link_to_name = link_to_name - self.use_alter = use_alter - self.match = match - - if len(set(columns)) != len(refcolumns): - if len(set(columns)) != len(columns): - # e.g. FOREIGN KEY (a, a) REFERENCES r (b, c) - raise exc.ArgumentError( - "ForeignKeyConstraint with duplicate source column " - "references are not supported." - ) - else: - # e.g. FOREIGN KEY (a) REFERENCES r (b, c) - # paraphrasing - # https://www.postgresql.org/docs/current/static/ddl-constraints.html - raise exc.ArgumentError( - "ForeignKeyConstraint number " - "of constrained columns must match the number of " - "referenced columns." - ) - - # standalone ForeignKeyConstraint - create - # associated ForeignKey objects which will be applied to hosted - # Column objects (in col.foreign_keys), either now or when attached - # to the Table for string-specified names - self.elements = [ - ForeignKey( - refcol, - _constraint=self, - name=self.name, - onupdate=self.onupdate, - ondelete=self.ondelete, - use_alter=self.use_alter, - link_to_name=self.link_to_name, - match=self.match, - deferrable=self.deferrable, - initially=self.initially, - **self.dialect_kwargs, - ) - for refcol in refcolumns - ] - - ColumnCollectionMixin.__init__(self, *columns) - if table is not None: - if hasattr(self, "parent"): - assert table is self.parent - self._set_parent_with_dispatch(table) - - def _append_element(self, column: Column[Any], fk: ForeignKey) -> None: - self._columns.add(column) - self.elements.append(fk) - - columns: ReadOnlyColumnCollection[str, Column[Any]] - """A :class:`_expression.ColumnCollection` representing the set of columns - for this constraint. - - """ - - elements: List[ForeignKey] - """A sequence of :class:`_schema.ForeignKey` objects. - - Each :class:`_schema.ForeignKey` - represents a single referring column/referred - column pair. - - This collection is intended to be read-only. - - """ - - @property - def _elements(self) -> util.OrderedDict[str, ForeignKey]: - # legacy - provide a dictionary view of (column_key, fk) - return util.OrderedDict(zip(self.column_keys, self.elements)) - - @property - def _referred_schema(self) -> Optional[str]: - for elem in self.elements: - return elem._referred_schema - else: - return None - - @property - def referred_table(self) -> Table: - """The :class:`_schema.Table` object to which this - :class:`_schema.ForeignKeyConstraint` references. - - This is a dynamically calculated attribute which may not be available - if the constraint and/or parent table is not yet associated with - a metadata collection that contains the referred table. - - """ - return self.elements[0].column.table - - def _validate_dest_table(self, table: Table) -> None: - table_keys = {elem._table_key() for elem in self.elements} - if None not in table_keys and len(table_keys) > 1: - elem0, elem1 = sorted(table_keys)[0:2] - raise exc.ArgumentError( - f"ForeignKeyConstraint on " - f"{table.fullname}({self._col_description}) refers to " - f"multiple remote tables: {elem0} and {elem1}" - ) - - @property - def column_keys(self) -> _typing_Sequence[str]: - """Return a list of string keys representing the local - columns in this :class:`_schema.ForeignKeyConstraint`. - - This list is either the original string arguments sent - to the constructor of the :class:`_schema.ForeignKeyConstraint`, - or if the constraint has been initialized with :class:`_schema.Column` - objects, is the string ``.key`` of each element. - - """ - if hasattr(self, "parent"): - return self._columns.keys() - else: - return [ - col.key if isinstance(col, ColumnElement) else str(col) - for col in self._pending_colargs - ] - - @property - def _col_description(self) -> str: - return ", ".join(self.column_keys) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - table = parent - assert isinstance(table, Table) - Constraint._set_parent(self, table) - - ColumnCollectionConstraint._set_parent(self, table) - - for col, fk in zip(self._columns, self.elements): - if not hasattr(fk, "parent") or fk.parent is not col: - fk._set_parent_with_dispatch(col) - - self._validate_dest_table(table) - - @util.deprecated( - "1.4", - "The :meth:`_schema.ForeignKeyConstraint.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy( - self, - *, - schema: Optional[str] = None, - target_table: Optional[Table] = None, - **kw: Any, - ) -> ForeignKeyConstraint: - return self._copy(schema=schema, target_table=target_table, **kw) - - def _copy( - self, - *, - schema: Optional[str] = None, - target_table: Optional[Table] = None, - **kw: Any, - ) -> ForeignKeyConstraint: - fkc = ForeignKeyConstraint( - [x.parent.key for x in self.elements], - [ - x._get_colspec( - schema=schema, - table_name=( - target_table.name - if target_table is not None - and x._table_key() == x.parent.table.key - else None - ), - _is_copy=True, - ) - for x in self.elements - ], - name=self.name, - onupdate=self.onupdate, - ondelete=self.ondelete, - use_alter=self.use_alter, - deferrable=self.deferrable, - initially=self.initially, - link_to_name=self.link_to_name, - match=self.match, - comment=self.comment, - ) - for self_fk, other_fk in zip(self.elements, fkc.elements): - self_fk._schema_item_copy(other_fk) - return self._schema_item_copy(fkc) - - -class PrimaryKeyConstraint(ColumnCollectionConstraint): - """A table-level PRIMARY KEY constraint. - - The :class:`.PrimaryKeyConstraint` object is present automatically - on any :class:`_schema.Table` object; it is assigned a set of - :class:`_schema.Column` objects corresponding to those marked with - the :paramref:`_schema.Column.primary_key` flag:: - - >>> my_table = Table('mytable', metadata, - ... Column('id', Integer, primary_key=True), - ... Column('version_id', Integer, primary_key=True), - ... Column('data', String(50)) - ... ) - >>> my_table.primary_key - PrimaryKeyConstraint( - Column('id', Integer(), table=<mytable>, - primary_key=True, nullable=False), - Column('version_id', Integer(), table=<mytable>, - primary_key=True, nullable=False) - ) - - The primary key of a :class:`_schema.Table` can also be specified by using - a :class:`.PrimaryKeyConstraint` object explicitly; in this mode of usage, - the "name" of the constraint can also be specified, as well as other - options which may be recognized by dialects:: - - my_table = Table('mytable', metadata, - Column('id', Integer), - Column('version_id', Integer), - Column('data', String(50)), - PrimaryKeyConstraint('id', 'version_id', - name='mytable_pk') - ) - - The two styles of column-specification should generally not be mixed. - An warning is emitted if the columns present in the - :class:`.PrimaryKeyConstraint` - don't match the columns that were marked as ``primary_key=True``, if both - are present; in this case, the columns are taken strictly from the - :class:`.PrimaryKeyConstraint` declaration, and those columns otherwise - marked as ``primary_key=True`` are ignored. This behavior is intended to - be backwards compatible with previous behavior. - - For the use case where specific options are to be specified on the - :class:`.PrimaryKeyConstraint`, but the usual style of using - ``primary_key=True`` flags is still desirable, an empty - :class:`.PrimaryKeyConstraint` may be specified, which will take on the - primary key column collection from the :class:`_schema.Table` based on the - flags:: - - my_table = Table('mytable', metadata, - Column('id', Integer, primary_key=True), - Column('version_id', Integer, primary_key=True), - Column('data', String(50)), - PrimaryKeyConstraint(name='mytable_pk', - mssql_clustered=True) - ) - - """ - - __visit_name__ = "primary_key_constraint" - - def __init__( - self, - *columns: _DDLColumnArgument, - name: Optional[str] = None, - deferrable: Optional[bool] = None, - initially: Optional[str] = None, - info: Optional[_InfoType] = None, - _implicit_generated: bool = False, - **dialect_kw: Any, - ) -> None: - self._implicit_generated = _implicit_generated - super().__init__( - *columns, - name=name, - deferrable=deferrable, - initially=initially, - info=info, - **dialect_kw, - ) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - table = parent - assert isinstance(table, Table) - super()._set_parent(table) - - if table.primary_key is not self: - table.constraints.discard(table.primary_key) - table.primary_key = self # type: ignore - table.constraints.add(self) - - table_pks = [c for c in table.c if c.primary_key] - if ( - self._columns - and table_pks - and set(table_pks) != set(self._columns) - ): - # black could not format these inline - table_pk_str = ", ".join("'%s'" % c.name for c in table_pks) - col_str = ", ".join("'%s'" % c.name for c in self._columns) - - util.warn( - f"Table '{table.name}' specifies columns " - f"{table_pk_str} as " - f"primary_key=True, " - f"not matching locally specified columns {col_str}; " - f"setting the " - f"current primary key columns to " - f"{col_str}. " - f"This warning " - f"may become an exception in a future release" - ) - table_pks[:] = [] - - for c in self._columns: - c.primary_key = True - if c._user_defined_nullable is NULL_UNSPECIFIED: - c.nullable = False - if table_pks: - self._columns.extend(table_pks) - - def _reload(self, columns: Iterable[Column[Any]]) -> None: - """repopulate this :class:`.PrimaryKeyConstraint` given - a set of columns. - - Existing columns in the table that are marked as primary_key=True - are maintained. - - Also fires a new event. - - This is basically like putting a whole new - :class:`.PrimaryKeyConstraint` object on the parent - :class:`_schema.Table` object without actually replacing the object. - - The ordering of the given list of columns is also maintained; these - columns will be appended to the list of columns after any which - are already present. - - """ - # set the primary key flag on new columns. - # note any existing PK cols on the table also have their - # flag still set. - for col in columns: - col.primary_key = True - - self._columns.extend(columns) - - PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore - self._set_parent_with_dispatch(self.table) - - def _replace(self, col: Column[Any]) -> None: - PrimaryKeyConstraint._autoincrement_column._reset(self) # type: ignore - self._columns.replace(col) - - self.dispatch._sa_event_column_added_to_pk_constraint(self, col) - - @property - def columns_autoinc_first(self) -> List[Column[Any]]: - autoinc = self._autoincrement_column - - if autoinc is not None: - return [autoinc] + [c for c in self._columns if c is not autoinc] - else: - return list(self._columns) - - @util.ro_memoized_property - def _autoincrement_column(self) -> Optional[Column[int]]: - def _validate_autoinc(col: Column[Any], autoinc_true: bool) -> bool: - if col.type._type_affinity is None or not issubclass( - col.type._type_affinity, - ( - type_api.INTEGERTYPE._type_affinity, - type_api.NUMERICTYPE._type_affinity, - ), - ): - if autoinc_true: - raise exc.ArgumentError( - f"Column type {col.type} on column '{col}' is not " - f"compatible with autoincrement=True" - ) - else: - return False - elif ( - not isinstance(col.default, (type(None), Sequence)) - and not autoinc_true - ): - return False - elif ( - col.server_default is not None - and not isinstance(col.server_default, Identity) - and not autoinc_true - ): - return False - elif col.foreign_keys and col.autoincrement not in ( - True, - "ignore_fk", - ): - return False - return True - - if len(self._columns) == 1: - col = list(self._columns)[0] - - if col.autoincrement is True: - _validate_autoinc(col, True) - return col - elif col.autoincrement in ( - "auto", - "ignore_fk", - ) and _validate_autoinc(col, False): - return col - else: - return None - - else: - autoinc = None - for col in self._columns: - if col.autoincrement is True: - _validate_autoinc(col, True) - if autoinc is not None: - raise exc.ArgumentError( - f"Only one Column may be marked " - f"autoincrement=True, found both " - f"{col.name} and {autoinc.name}." - ) - else: - autoinc = col - - return autoinc - - -class UniqueConstraint(ColumnCollectionConstraint): - """A table-level UNIQUE constraint. - - Defines a single column or composite UNIQUE constraint. For a no-frills, - single column constraint, adding ``unique=True`` to the ``Column`` - definition is a shorthand equivalent for an unnamed, single column - UniqueConstraint. - """ - - __visit_name__ = "unique_constraint" - - -class Index( - DialectKWArgs, ColumnCollectionMixin, HasConditionalDDL, SchemaItem -): - """A table-level INDEX. - - Defines a composite (one or more column) INDEX. - - E.g.:: - - sometable = Table("sometable", metadata, - Column("name", String(50)), - Column("address", String(100)) - ) - - Index("some_index", sometable.c.name) - - For a no-frills, single column index, adding - :class:`_schema.Column` also supports ``index=True``:: - - sometable = Table("sometable", metadata, - Column("name", String(50), index=True) - ) - - For a composite index, multiple columns can be specified:: - - Index("some_index", sometable.c.name, sometable.c.address) - - Functional indexes are supported as well, typically by using the - :data:`.func` construct in conjunction with table-bound - :class:`_schema.Column` objects:: - - Index("some_index", func.lower(sometable.c.name)) - - An :class:`.Index` can also be manually associated with a - :class:`_schema.Table`, - either through inline declaration or using - :meth:`_schema.Table.append_constraint`. When this approach is used, - the names - of the indexed columns can be specified as strings:: - - Table("sometable", metadata, - Column("name", String(50)), - Column("address", String(100)), - Index("some_index", "name", "address") - ) - - To support functional or expression-based indexes in this form, the - :func:`_expression.text` construct may be used:: - - from sqlalchemy import text - - Table("sometable", metadata, - Column("name", String(50)), - Column("address", String(100)), - Index("some_index", text("lower(name)")) - ) - - .. seealso:: - - :ref:`schema_indexes` - General information on :class:`.Index`. - - :ref:`postgresql_indexes` - PostgreSQL-specific options available for - the :class:`.Index` construct. - - :ref:`mysql_indexes` - MySQL-specific options available for the - :class:`.Index` construct. - - :ref:`mssql_indexes` - MSSQL-specific options available for the - :class:`.Index` construct. - - """ - - __visit_name__ = "index" - - table: Optional[Table] - expressions: _typing_Sequence[Union[str, ColumnElement[Any]]] - _table_bound_expressions: _typing_Sequence[ColumnElement[Any]] - - def __init__( - self, - name: Optional[str], - *expressions: _DDLColumnArgument, - unique: bool = False, - quote: Optional[bool] = None, - info: Optional[_InfoType] = None, - _table: Optional[Table] = None, - _column_flag: bool = False, - **dialect_kw: Any, - ) -> None: - r"""Construct an index object. - - :param name: - The name of the index - - :param \*expressions: - Column expressions to include in the index. The expressions - are normally instances of :class:`_schema.Column`, but may also - be arbitrary SQL expressions which ultimately refer to a - :class:`_schema.Column`. - - :param unique=False: - Keyword only argument; if True, create a unique index. - - :param quote=None: - Keyword only argument; whether to apply quoting to the name of - the index. Works in the same manner as that of - :paramref:`_schema.Column.quote`. - - :param info=None: Optional data dictionary which will be populated - into the :attr:`.SchemaItem.info` attribute of this object. - - :param \**dialect_kw: Additional keyword arguments not mentioned above - are dialect specific, and passed in the form - ``<dialectname>_<argname>``. See the documentation regarding an - individual dialect at :ref:`dialect_toplevel` for detail on - documented arguments. - - """ - self.table = table = None - - self.name = quoted_name.construct(name, quote) - self.unique = unique - if info is not None: - self.info = info - - # TODO: consider "table" argument being public, but for - # the purpose of the fix here, it starts as private. - if _table is not None: - table = _table - - self._validate_dialect_kwargs(dialect_kw) - - self.expressions = [] - # will call _set_parent() if table-bound column - # objects are present - ColumnCollectionMixin.__init__( - self, - *expressions, - _column_flag=_column_flag, - _gather_expressions=self.expressions, - ) - if table is not None: - self._set_parent(table) - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - table = parent - assert isinstance(table, Table) - ColumnCollectionMixin._set_parent(self, table) - - if self.table is not None and table is not self.table: - raise exc.ArgumentError( - f"Index '{self.name}' is against table " - f"'{self.table.description}', and " - f"cannot be associated with table '{table.description}'." - ) - self.table = table - table.indexes.add(self) - - expressions = self.expressions - col_expressions = self._col_expressions(table) - assert len(expressions) == len(col_expressions) - - exprs = [] - for expr, colexpr in zip(expressions, col_expressions): - if isinstance(expr, ClauseElement): - exprs.append(expr) - elif colexpr is not None: - exprs.append(colexpr) - else: - assert False - self.expressions = self._table_bound_expressions = exprs - - def create(self, bind: _CreateDropBind, checkfirst: bool = False) -> None: - """Issue a ``CREATE`` statement for this - :class:`.Index`, using the given - :class:`.Connection` or :class:`.Engine`` for connectivity. - - .. seealso:: - - :meth:`_schema.MetaData.create_all`. - - """ - bind._run_ddl_visitor(ddl.SchemaGenerator, self, checkfirst=checkfirst) - - def drop(self, bind: _CreateDropBind, checkfirst: bool = False) -> None: - """Issue a ``DROP`` statement for this - :class:`.Index`, using the given - :class:`.Connection` or :class:`.Engine` for connectivity. - - .. seealso:: - - :meth:`_schema.MetaData.drop_all`. - - """ - bind._run_ddl_visitor(ddl.SchemaDropper, self, checkfirst=checkfirst) - - def __repr__(self) -> str: - exprs: _typing_Sequence[Any] # noqa: F842 - - return "Index(%s)" % ( - ", ".join( - [repr(self.name)] - + [repr(e) for e in self.expressions] - + (self.unique and ["unique=True"] or []) - ) - ) - - -_NamingSchemaCallable = Callable[[Constraint, Table], str] -_NamingSchemaDirective = Union[str, _NamingSchemaCallable] - - -class _NamingSchemaTD(TypedDict, total=False): - fk: _NamingSchemaDirective - pk: _NamingSchemaDirective - ix: _NamingSchemaDirective - ck: _NamingSchemaDirective - uq: _NamingSchemaDirective - - -_NamingSchemaParameter = Union[ - # it seems like the TypedDict here is useful for pylance typeahead, - # and not much else - _NamingSchemaTD, - # there is no form that allows Union[Type[Any], str] to work in all - # cases, including breaking out Mapping[] entries for each combination - # even, therefore keys must be `Any` (see #10264) - Mapping[Any, _NamingSchemaDirective], -] - - -DEFAULT_NAMING_CONVENTION: _NamingSchemaParameter = util.immutabledict( - {"ix": "ix_%(column_0_label)s"} -) - - -class MetaData(HasSchemaAttr): - """A collection of :class:`_schema.Table` - objects and their associated schema - constructs. - - Holds a collection of :class:`_schema.Table` objects as well as - an optional binding to an :class:`_engine.Engine` or - :class:`_engine.Connection`. If bound, the :class:`_schema.Table` objects - in the collection and their columns may participate in implicit SQL - execution. - - The :class:`_schema.Table` objects themselves are stored in the - :attr:`_schema.MetaData.tables` dictionary. - - :class:`_schema.MetaData` is a thread-safe object for read operations. - Construction of new tables within a single :class:`_schema.MetaData` - object, - either explicitly or via reflection, may not be completely thread-safe. - - .. seealso:: - - :ref:`metadata_describing` - Introduction to database metadata - - """ - - __visit_name__ = "metadata" - - def __init__( - self, - schema: Optional[str] = None, - quote_schema: Optional[bool] = None, - naming_convention: Optional[_NamingSchemaParameter] = None, - info: Optional[_InfoType] = None, - ) -> None: - """Create a new MetaData object. - - :param schema: - The default schema to use for the :class:`_schema.Table`, - :class:`.Sequence`, and potentially other objects associated with - this :class:`_schema.MetaData`. Defaults to ``None``. - - .. seealso:: - - :ref:`schema_metadata_schema_name` - details on how the - :paramref:`_schema.MetaData.schema` parameter is used. - - :paramref:`_schema.Table.schema` - - :paramref:`.Sequence.schema` - - :param quote_schema: - Sets the ``quote_schema`` flag for those :class:`_schema.Table`, - :class:`.Sequence`, and other objects which make usage of the - local ``schema`` name. - - :param info: Optional data dictionary which will be populated into the - :attr:`.SchemaItem.info` attribute of this object. - - :param naming_convention: a dictionary referring to values which - will establish default naming conventions for :class:`.Constraint` - and :class:`.Index` objects, for those objects which are not given - a name explicitly. - - The keys of this dictionary may be: - - * a constraint or Index class, e.g. the :class:`.UniqueConstraint`, - :class:`_schema.ForeignKeyConstraint` class, the :class:`.Index` - class - - * a string mnemonic for one of the known constraint classes; - ``"fk"``, ``"pk"``, ``"ix"``, ``"ck"``, ``"uq"`` for foreign key, - primary key, index, check, and unique constraint, respectively. - - * the string name of a user-defined "token" that can be used - to define new naming tokens. - - The values associated with each "constraint class" or "constraint - mnemonic" key are string naming templates, such as - ``"uq_%(table_name)s_%(column_0_name)s"``, - which describe how the name should be composed. The values - associated with user-defined "token" keys should be callables of the - form ``fn(constraint, table)``, which accepts the constraint/index - object and :class:`_schema.Table` as arguments, returning a string - result. - - The built-in names are as follows, some of which may only be - available for certain types of constraint: - - * ``%(table_name)s`` - the name of the :class:`_schema.Table` - object - associated with the constraint. - - * ``%(referred_table_name)s`` - the name of the - :class:`_schema.Table` - object associated with the referencing target of a - :class:`_schema.ForeignKeyConstraint`. - - * ``%(column_0_name)s`` - the name of the :class:`_schema.Column` - at - index position "0" within the constraint. - - * ``%(column_0N_name)s`` - the name of all :class:`_schema.Column` - objects in order within the constraint, joined without a - separator. - - * ``%(column_0_N_name)s`` - the name of all - :class:`_schema.Column` - objects in order within the constraint, joined with an - underscore as a separator. - - * ``%(column_0_label)s``, ``%(column_0N_label)s``, - ``%(column_0_N_label)s`` - the label of either the zeroth - :class:`_schema.Column` or all :class:`.Columns`, separated with - or without an underscore - - * ``%(column_0_key)s``, ``%(column_0N_key)s``, - ``%(column_0_N_key)s`` - the key of either the zeroth - :class:`_schema.Column` or all :class:`.Columns`, separated with - or without an underscore - - * ``%(referred_column_0_name)s``, ``%(referred_column_0N_name)s`` - ``%(referred_column_0_N_name)s``, ``%(referred_column_0_key)s``, - ``%(referred_column_0N_key)s``, ... column tokens which - render the names/keys/labels of columns that are referenced - by a :class:`_schema.ForeignKeyConstraint`. - - * ``%(constraint_name)s`` - a special key that refers to the - existing name given to the constraint. When this key is - present, the :class:`.Constraint` object's existing name will be - replaced with one that is composed from template string that - uses this token. When this token is present, it is required that - the :class:`.Constraint` is given an explicit name ahead of time. - - * user-defined: any additional token may be implemented by passing - it along with a ``fn(constraint, table)`` callable to the - naming_convention dictionary. - - .. versionadded:: 1.3.0 - added new ``%(column_0N_name)s``, - ``%(column_0_N_name)s``, and related tokens that produce - concatenations of names, keys, or labels for all columns referred - to by a given constraint. - - .. seealso:: - - :ref:`constraint_naming_conventions` - for detailed usage - examples. - - """ - if schema is not None and not isinstance(schema, str): - raise exc.ArgumentError( - "expected schema argument to be a string, " - f"got {type(schema)}." - ) - self.tables = util.FacadeDict() - self.schema = quoted_name.construct(schema, quote_schema) - self.naming_convention = ( - naming_convention - if naming_convention - else DEFAULT_NAMING_CONVENTION - ) - if info: - self.info = info - self._schemas: Set[str] = set() - self._sequences: Dict[str, Sequence] = {} - self._fk_memos: Dict[Tuple[str, Optional[str]], List[ForeignKey]] = ( - collections.defaultdict(list) - ) - - tables: util.FacadeDict[str, Table] - """A dictionary of :class:`_schema.Table` - objects keyed to their name or "table key". - - The exact key is that determined by the :attr:`_schema.Table.key` - attribute; - for a table with no :attr:`_schema.Table.schema` attribute, - this is the same - as :attr:`_schema.Table.name`. For a table with a schema, - it is typically of the - form ``schemaname.tablename``. - - .. seealso:: - - :attr:`_schema.MetaData.sorted_tables` - - """ - - def __repr__(self) -> str: - return "MetaData()" - - def __contains__(self, table_or_key: Union[str, Table]) -> bool: - if not isinstance(table_or_key, str): - table_or_key = table_or_key.key - return table_or_key in self.tables - - def _add_table( - self, name: str, schema: Optional[str], table: Table - ) -> None: - key = _get_table_key(name, schema) - self.tables._insert_item(key, table) - if schema: - self._schemas.add(schema) - - def _remove_table(self, name: str, schema: Optional[str]) -> None: - key = _get_table_key(name, schema) - removed = dict.pop(self.tables, key, None) - if removed is not None: - for fk in removed.foreign_keys: - fk._remove_from_metadata(self) - if self._schemas: - self._schemas = { - t.schema for t in self.tables.values() if t.schema is not None - } - - def __getstate__(self) -> Dict[str, Any]: - return { - "tables": self.tables, - "schema": self.schema, - "schemas": self._schemas, - "sequences": self._sequences, - "fk_memos": self._fk_memos, - "naming_convention": self.naming_convention, - } - - def __setstate__(self, state: Dict[str, Any]) -> None: - self.tables = state["tables"] - self.schema = state["schema"] - self.naming_convention = state["naming_convention"] - self._sequences = state["sequences"] - self._schemas = state["schemas"] - self._fk_memos = state["fk_memos"] - - def clear(self) -> None: - """Clear all Table objects from this MetaData.""" - - dict.clear(self.tables) # type: ignore - self._schemas.clear() - self._fk_memos.clear() - - def remove(self, table: Table) -> None: - """Remove the given Table object from this MetaData.""" - - self._remove_table(table.name, table.schema) - - @property - def sorted_tables(self) -> List[Table]: - """Returns a list of :class:`_schema.Table` objects sorted in order of - foreign key dependency. - - The sorting will place :class:`_schema.Table` - objects that have dependencies - first, before the dependencies themselves, representing the - order in which they can be created. To get the order in which - the tables would be dropped, use the ``reversed()`` Python built-in. - - .. warning:: - - The :attr:`.MetaData.sorted_tables` attribute cannot by itself - accommodate automatic resolution of dependency cycles between - tables, which are usually caused by mutually dependent foreign key - constraints. When these cycles are detected, the foreign keys - of these tables are omitted from consideration in the sort. - A warning is emitted when this condition occurs, which will be an - exception raise in a future release. Tables which are not part - of the cycle will still be returned in dependency order. - - To resolve these cycles, the - :paramref:`_schema.ForeignKeyConstraint.use_alter` parameter may be - applied to those constraints which create a cycle. Alternatively, - the :func:`_schema.sort_tables_and_constraints` function will - automatically return foreign key constraints in a separate - collection when cycles are detected so that they may be applied - to a schema separately. - - .. versionchanged:: 1.3.17 - a warning is emitted when - :attr:`.MetaData.sorted_tables` cannot perform a proper sort - due to cyclical dependencies. This will be an exception in a - future release. Additionally, the sort will continue to return - other tables not involved in the cycle in dependency order which - was not the case previously. - - .. seealso:: - - :func:`_schema.sort_tables` - - :func:`_schema.sort_tables_and_constraints` - - :attr:`_schema.MetaData.tables` - - :meth:`_reflection.Inspector.get_table_names` - - :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names` - - - """ - return ddl.sort_tables( - sorted(self.tables.values(), key=lambda t: t.key) # type: ignore - ) - - @util.preload_module("sqlalchemy.engine.reflection") - def reflect( - self, - bind: Union[Engine, Connection], - schema: Optional[str] = None, - views: bool = False, - only: Union[ - _typing_Sequence[str], Callable[[str, MetaData], bool], None - ] = None, - extend_existing: bool = False, - autoload_replace: bool = True, - resolve_fks: bool = True, - **dialect_kwargs: Any, - ) -> None: - r"""Load all available table definitions from the database. - - Automatically creates ``Table`` entries in this ``MetaData`` for any - table available in the database but not yet present in the - ``MetaData``. May be called multiple times to pick up tables recently - added to the database, however no special action is taken if a table - in this ``MetaData`` no longer exists in the database. - - :param bind: - A :class:`.Connection` or :class:`.Engine` used to access the - database. - - :param schema: - Optional, query and reflect tables from an alternate schema. - If None, the schema associated with this :class:`_schema.MetaData` - is used, if any. - - :param views: - If True, also reflect views (materialized and plain). - - :param only: - Optional. Load only a sub-set of available named tables. May be - specified as a sequence of names or a callable. - - If a sequence of names is provided, only those tables will be - reflected. An error is raised if a table is requested but not - available. Named tables already present in this ``MetaData`` are - ignored. - - If a callable is provided, it will be used as a boolean predicate to - filter the list of potential table names. The callable is called - with a table name and this ``MetaData`` instance as positional - arguments and should return a true value for any table to reflect. - - :param extend_existing: Passed along to each :class:`_schema.Table` as - :paramref:`_schema.Table.extend_existing`. - - :param autoload_replace: Passed along to each :class:`_schema.Table` - as - :paramref:`_schema.Table.autoload_replace`. - - :param resolve_fks: if True, reflect :class:`_schema.Table` - objects linked - to :class:`_schema.ForeignKey` objects located in each - :class:`_schema.Table`. - For :meth:`_schema.MetaData.reflect`, - this has the effect of reflecting - related tables that might otherwise not be in the list of tables - being reflected, for example if the referenced table is in a - different schema or is omitted via the - :paramref:`.MetaData.reflect.only` parameter. When False, - :class:`_schema.ForeignKey` objects are not followed to the - :class:`_schema.Table` - in which they link, however if the related table is also part of the - list of tables that would be reflected in any case, the - :class:`_schema.ForeignKey` object will still resolve to its related - :class:`_schema.Table` after the :meth:`_schema.MetaData.reflect` - operation is - complete. Defaults to True. - - .. versionadded:: 1.3.0 - - .. seealso:: - - :paramref:`_schema.Table.resolve_fks` - - :param \**dialect_kwargs: Additional keyword arguments not mentioned - above are dialect specific, and passed in the form - ``<dialectname>_<argname>``. See the documentation regarding an - individual dialect at :ref:`dialect_toplevel` for detail on - documented arguments. - - .. seealso:: - - :ref:`metadata_reflection_toplevel` - - :meth:`_events.DDLEvents.column_reflect` - Event used to customize - the reflected columns. Usually used to generalize the types using - :meth:`_types.TypeEngine.as_generic` - - :ref:`metadata_reflection_dbagnostic_types` - describes how to - reflect tables using general types. - - """ - - with inspection.inspect(bind)._inspection_context() as insp: - reflect_opts: Any = { - "autoload_with": insp, - "extend_existing": extend_existing, - "autoload_replace": autoload_replace, - "resolve_fks": resolve_fks, - "_extend_on": set(), - } - - reflect_opts.update(dialect_kwargs) - - if schema is None: - schema = self.schema - - if schema is not None: - reflect_opts["schema"] = schema - - kind = util.preloaded.engine_reflection.ObjectKind.TABLE - available: util.OrderedSet[str] = util.OrderedSet( - insp.get_table_names(schema) - ) - if views: - kind = util.preloaded.engine_reflection.ObjectKind.ANY - available.update(insp.get_view_names(schema)) - try: - available.update(insp.get_materialized_view_names(schema)) - except NotImplementedError: - pass - - if schema is not None: - available_w_schema: util.OrderedSet[str] = util.OrderedSet( - [f"{schema}.{name}" for name in available] - ) - else: - available_w_schema = available - - current = set(self.tables) - - if only is None: - load = [ - name - for name, schname in zip(available, available_w_schema) - if extend_existing or schname not in current - ] - elif callable(only): - load = [ - name - for name, schname in zip(available, available_w_schema) - if (extend_existing or schname not in current) - and only(name, self) - ] - else: - missing = [name for name in only if name not in available] - if missing: - s = schema and (" schema '%s'" % schema) or "" - missing_str = ", ".join(missing) - raise exc.InvalidRequestError( - f"Could not reflect: requested table(s) not available " - f"in {bind.engine!r}{s}: ({missing_str})" - ) - load = [ - name - for name in only - if extend_existing or name not in current - ] - # pass the available tables so the inspector can - # choose to ignore the filter_names - _reflect_info = insp._get_reflection_info( - schema=schema, - filter_names=load, - available=available, - kind=kind, - scope=util.preloaded.engine_reflection.ObjectScope.ANY, - **dialect_kwargs, - ) - reflect_opts["_reflect_info"] = _reflect_info - - for name in load: - try: - Table(name, self, **reflect_opts) - except exc.UnreflectableTableError as uerr: - util.warn(f"Skipping table {name}: {uerr}") - - def create_all( - self, - bind: _CreateDropBind, - tables: Optional[_typing_Sequence[Table]] = None, - checkfirst: bool = True, - ) -> None: - """Create all tables stored in this metadata. - - Conditional by default, will not attempt to recreate tables already - present in the target database. - - :param bind: - A :class:`.Connection` or :class:`.Engine` used to access the - database. - - :param tables: - Optional list of ``Table`` objects, which is a subset of the total - tables in the ``MetaData`` (others are ignored). - - :param checkfirst: - Defaults to True, don't issue CREATEs for tables already present - in the target database. - - """ - bind._run_ddl_visitor( - ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables - ) - - def drop_all( - self, - bind: _CreateDropBind, - tables: Optional[_typing_Sequence[Table]] = None, - checkfirst: bool = True, - ) -> None: - """Drop all tables stored in this metadata. - - Conditional by default, will not attempt to drop tables not present in - the target database. - - :param bind: - A :class:`.Connection` or :class:`.Engine` used to access the - database. - - :param tables: - Optional list of ``Table`` objects, which is a subset of the - total tables in the ``MetaData`` (others are ignored). - - :param checkfirst: - Defaults to True, only issue DROPs for tables confirmed to be - present in the target database. - - """ - bind._run_ddl_visitor( - ddl.SchemaDropper, self, checkfirst=checkfirst, tables=tables - ) - - -class Computed(FetchedValue, SchemaItem): - """Defines a generated column, i.e. "GENERATED ALWAYS AS" syntax. - - The :class:`.Computed` construct is an inline construct added to the - argument list of a :class:`_schema.Column` object:: - - from sqlalchemy import Computed - - Table('square', metadata_obj, - Column('side', Float, nullable=False), - Column('area', Float, Computed('side * side')) - ) - - See the linked documentation below for complete details. - - .. versionadded:: 1.3.11 - - .. seealso:: - - :ref:`computed_ddl` - - """ - - __visit_name__ = "computed_column" - - column: Optional[Column[Any]] - - @_document_text_coercion( - "sqltext", ":class:`.Computed`", ":paramref:`.Computed.sqltext`" - ) - def __init__( - self, sqltext: _DDLColumnArgument, persisted: Optional[bool] = None - ) -> None: - """Construct a GENERATED ALWAYS AS DDL construct to accompany a - :class:`_schema.Column`. - - :param sqltext: - A string containing the column generation expression, which will be - used verbatim, or a SQL expression construct, such as a - :func:`_expression.text` - object. If given as a string, the object is converted to a - :func:`_expression.text` object. - - :param persisted: - Optional, controls how this column should be persisted by the - database. Possible values are: - - * ``None``, the default, it will use the default persistence - defined by the database. - * ``True``, will render ``GENERATED ALWAYS AS ... STORED``, or the - equivalent for the target database if supported. - * ``False``, will render ``GENERATED ALWAYS AS ... VIRTUAL``, or - the equivalent for the target database if supported. - - Specifying ``True`` or ``False`` may raise an error when the DDL - is emitted to the target database if the database does not support - that persistence option. Leaving this parameter at its default - of ``None`` is guaranteed to succeed for all databases that support - ``GENERATED ALWAYS AS``. - - """ - self.sqltext = coercions.expect(roles.DDLExpressionRole, sqltext) - self.persisted = persisted - self.column = None - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, Column) - - if not isinstance( - parent.server_default, (type(None), Computed) - ) or not isinstance(parent.server_onupdate, (type(None), Computed)): - raise exc.ArgumentError( - "A generated column cannot specify a server_default or a " - "server_onupdate argument" - ) - self.column = parent - parent.computed = self - self.column.server_onupdate = self - self.column.server_default = self - - def _as_for_update(self, for_update: bool) -> FetchedValue: - return self - - @util.deprecated( - "1.4", - "The :meth:`_schema.Computed.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy( - self, *, target_table: Optional[Table] = None, **kw: Any - ) -> Computed: - return self._copy(target_table=target_table, **kw) - - def _copy( - self, *, target_table: Optional[Table] = None, **kw: Any - ) -> Computed: - sqltext = _copy_expression( - self.sqltext, - self.column.table if self.column is not None else None, - target_table, - ) - g = Computed(sqltext, persisted=self.persisted) - - return self._schema_item_copy(g) - - -class Identity(IdentityOptions, FetchedValue, SchemaItem): - """Defines an identity column, i.e. "GENERATED { ALWAYS | BY DEFAULT } - AS IDENTITY" syntax. - - The :class:`.Identity` construct is an inline construct added to the - argument list of a :class:`_schema.Column` object:: - - from sqlalchemy import Identity - - Table('foo', metadata_obj, - Column('id', Integer, Identity()) - Column('description', Text), - ) - - See the linked documentation below for complete details. - - .. versionadded:: 1.4 - - .. seealso:: - - :ref:`identity_ddl` - - """ - - __visit_name__ = "identity_column" - - is_identity = True - - def __init__( - self, - always: bool = False, - on_null: Optional[bool] = None, - start: Optional[int] = None, - increment: Optional[int] = None, - minvalue: Optional[int] = None, - maxvalue: Optional[int] = None, - nominvalue: Optional[bool] = None, - nomaxvalue: Optional[bool] = None, - cycle: Optional[bool] = None, - cache: Optional[int] = None, - order: Optional[bool] = None, - ) -> None: - """Construct a GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY DDL - construct to accompany a :class:`_schema.Column`. - - See the :class:`.Sequence` documentation for a complete description - of most parameters. - - .. note:: - MSSQL supports this construct as the preferred alternative to - generate an IDENTITY on a column, but it uses non standard - syntax that only support :paramref:`_schema.Identity.start` - and :paramref:`_schema.Identity.increment`. - All other parameters are ignored. - - :param always: - A boolean, that indicates the type of identity column. - If ``False`` is specified, the default, then the user-specified - value takes precedence. - If ``True`` is specified, a user-specified value is not accepted ( - on some backends, like PostgreSQL, OVERRIDING SYSTEM VALUE, or - similar, may be specified in an INSERT to override the sequence - value). - Some backends also have a default value for this parameter, - ``None`` can be used to omit rendering this part in the DDL. It - will be treated as ``False`` if a backend does not have a default - value. - - :param on_null: - Set to ``True`` to specify ON NULL in conjunction with a - ``always=False`` identity column. This option is only supported on - some backends, like Oracle. - - :param start: the starting index of the sequence. - :param increment: the increment value of the sequence. - :param minvalue: the minimum value of the sequence. - :param maxvalue: the maximum value of the sequence. - :param nominvalue: no minimum value of the sequence. - :param nomaxvalue: no maximum value of the sequence. - :param cycle: allows the sequence to wrap around when the maxvalue - or minvalue has been reached. - :param cache: optional integer value; number of future values in the - sequence which are calculated in advance. - :param order: optional boolean value; if true, renders the - ORDER keyword. - - """ - IdentityOptions.__init__( - self, - start=start, - increment=increment, - minvalue=minvalue, - maxvalue=maxvalue, - nominvalue=nominvalue, - nomaxvalue=nomaxvalue, - cycle=cycle, - cache=cache, - order=order, - ) - self.always = always - self.on_null = on_null - self.column = None - - def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None: - assert isinstance(parent, Column) - if not isinstance( - parent.server_default, (type(None), Identity) - ) or not isinstance(parent.server_onupdate, type(None)): - raise exc.ArgumentError( - "A column with an Identity object cannot specify a " - "server_default or a server_onupdate argument" - ) - if parent.autoincrement is False: - raise exc.ArgumentError( - "A column with an Identity object cannot specify " - "autoincrement=False" - ) - self.column = parent - - parent.identity = self - if parent._user_defined_nullable is NULL_UNSPECIFIED: - parent.nullable = False - - parent.server_default = self - - def _as_for_update(self, for_update: bool) -> FetchedValue: - return self - - @util.deprecated( - "1.4", - "The :meth:`_schema.Identity.copy` method is deprecated " - "and will be removed in a future release.", - ) - def copy(self, **kw: Any) -> Identity: - return self._copy(**kw) - - def _copy(self, **kw: Any) -> Identity: - i = Identity( - always=self.always, - on_null=self.on_null, - start=self.start, - increment=self.increment, - minvalue=self.minvalue, - maxvalue=self.maxvalue, - nominvalue=self.nominvalue, - nomaxvalue=self.nomaxvalue, - cycle=self.cycle, - cache=self.cache, - order=self.order, - ) - - return self._schema_item_copy(i) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py deleted file mode 100644 index 65978f6..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/selectable.py +++ /dev/null @@ -1,6913 +0,0 @@ -# sql/selectable.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""The :class:`_expression.FromClause` class of SQL expression elements, -representing -SQL tables and derived rowsets. - -""" - -from __future__ import annotations - -import collections -from enum import Enum -import itertools -from typing import AbstractSet -from typing import Any as TODO_Any -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import List -from typing import NamedTuple -from typing import NoReturn -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Set -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import cache_key -from . import coercions -from . import operators -from . import roles -from . import traversals -from . import type_api -from . import visitors -from ._typing import _ColumnsClauseArgument -from ._typing import _no_kw -from ._typing import _TP -from ._typing import is_column_element -from ._typing import is_select_statement -from ._typing import is_subquery -from ._typing import is_table -from ._typing import is_text_clause -from .annotation import Annotated -from .annotation import SupportsCloneAnnotations -from .base import _clone -from .base import _cloned_difference -from .base import _cloned_intersection -from .base import _entity_namespace_key -from .base import _EntityNamespace -from .base import _expand_cloned -from .base import _from_objects -from .base import _generative -from .base import _never_select_column -from .base import _NoArg -from .base import _select_iterables -from .base import CacheableOptions -from .base import ColumnCollection -from .base import ColumnSet -from .base import CompileState -from .base import DedupeColumnCollection -from .base import Executable -from .base import Generative -from .base import HasCompileState -from .base import HasMemoized -from .base import Immutable -from .coercions import _document_text_coercion -from .elements import _anonymous_label -from .elements import BindParameter -from .elements import BooleanClauseList -from .elements import ClauseElement -from .elements import ClauseList -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import DQLDMLClauseElement -from .elements import GroupedElement -from .elements import literal_column -from .elements import TableValuedColumn -from .elements import UnaryExpression -from .operators import OperatorType -from .sqltypes import NULLTYPE -from .visitors import _TraverseInternalsType -from .visitors import InternalTraversal -from .visitors import prefix_anon_map -from .. import exc -from .. import util -from ..util import HasMemoized_ro_memoized_attribute -from ..util.typing import Literal -from ..util.typing import Protocol -from ..util.typing import Self - -and_ = BooleanClauseList.and_ - -_T = TypeVar("_T", bound=Any) - -if TYPE_CHECKING: - from ._typing import _ColumnExpressionArgument - from ._typing import _ColumnExpressionOrStrLabelArgument - from ._typing import _FromClauseArgument - from ._typing import _JoinTargetArgument - from ._typing import _LimitOffsetType - from ._typing import _MAYBE_ENTITY - from ._typing import _NOT_ENTITY - from ._typing import _OnClauseArgument - from ._typing import _SelectStatementForCompoundArgument - from ._typing import _T0 - from ._typing import _T1 - from ._typing import _T2 - from ._typing import _T3 - from ._typing import _T4 - from ._typing import _T5 - from ._typing import _T6 - from ._typing import _T7 - from ._typing import _TextCoercedExpressionArgument - from ._typing import _TypedColumnClauseArgument as _TCCA - from ._typing import _TypeEngineArgument - from .base import _AmbiguousTableNameMap - from .base import ExecutableOption - from .base import ReadOnlyColumnCollection - from .cache_key import _CacheKeyTraversalType - from .compiler import SQLCompiler - from .dml import Delete - from .dml import Update - from .elements import BinaryExpression - from .elements import KeyedColumnElement - from .elements import Label - from .elements import NamedColumn - from .elements import TextClause - from .functions import Function - from .schema import ForeignKey - from .schema import ForeignKeyConstraint - from .sqltypes import TableValueType - from .type_api import TypeEngine - from .visitors import _CloneCallableType - - -_ColumnsClauseElement = Union["FromClause", ColumnElement[Any], "TextClause"] -_LabelConventionCallable = Callable[ - [Union["ColumnElement[Any]", "TextClause"]], Optional[str] -] - - -class _JoinTargetProtocol(Protocol): - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: ... - - @util.ro_non_memoized_property - def entity_namespace(self) -> _EntityNamespace: ... - - -_JoinTargetElement = Union["FromClause", _JoinTargetProtocol] -_OnClauseElement = Union["ColumnElement[bool]", _JoinTargetProtocol] - -_ForUpdateOfArgument = Union[ - # single column, Table, ORM Entity - Union[ - "_ColumnExpressionArgument[Any]", - "_FromClauseArgument", - ], - # or sequence of single column elements - Sequence["_ColumnExpressionArgument[Any]"], -] - - -_SetupJoinsElement = Tuple[ - _JoinTargetElement, - Optional[_OnClauseElement], - Optional["FromClause"], - Dict[str, Any], -] - - -_SelectIterable = Iterable[Union["ColumnElement[Any]", "TextClause"]] - - -class _OffsetLimitParam(BindParameter[int]): - inherit_cache = True - - @property - def _limit_offset_value(self) -> Optional[int]: - return self.effective_value - - -class ReturnsRows(roles.ReturnsRowsRole, DQLDMLClauseElement): - """The base-most class for Core constructs that have some concept of - columns that can represent rows. - - While the SELECT statement and TABLE are the primary things we think - of in this category, DML like INSERT, UPDATE and DELETE can also specify - RETURNING which means they can be used in CTEs and other forms, and - PostgreSQL has functions that return rows also. - - .. versionadded:: 1.4 - - """ - - _is_returns_rows = True - - # sub-elements of returns_rows - _is_from_clause = False - _is_select_base = False - _is_select_statement = False - _is_lateral = False - - @property - def selectable(self) -> ReturnsRows: - return self - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - """A sequence of column expression objects that represents the - "selected" columns of this :class:`_expression.ReturnsRows`. - - This is typically equivalent to .exported_columns except it is - delivered in the form of a straight sequence and not keyed - :class:`_expression.ColumnCollection`. - - """ - raise NotImplementedError() - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - """Return ``True`` if this :class:`.ReturnsRows` is - 'derived' from the given :class:`.FromClause`. - - An example would be an Alias of a Table is derived from that Table. - - """ - raise NotImplementedError() - - def _generate_fromclause_column_proxies( - self, fromclause: FromClause - ) -> None: - """Populate columns into an :class:`.AliasedReturnsRows` object.""" - - raise NotImplementedError() - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - """reset internal collections for an incoming column being added.""" - raise NotImplementedError() - - @property - def exported_columns(self) -> ReadOnlyColumnCollection[Any, Any]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.ReturnsRows`. - - The "exported" columns represent the collection of - :class:`_expression.ColumnElement` - expressions that are rendered by this SQL - construct. There are primary varieties which are the - "FROM clause columns" of a FROM clause, such as a table, join, - or subquery, the "SELECTed columns", which are the columns in - the "columns clause" of a SELECT statement, and the RETURNING - columns in a DML statement.. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.FromClause.exported_columns` - - :attr:`_expression.SelectBase.exported_columns` - """ - - raise NotImplementedError() - - -class ExecutableReturnsRows(Executable, ReturnsRows): - """base for executable statements that return rows.""" - - -class TypedReturnsRows(ExecutableReturnsRows, Generic[_TP]): - """base for executable statements that return rows.""" - - -class Selectable(ReturnsRows): - """Mark a class as being selectable.""" - - __visit_name__ = "selectable" - - is_selectable = True - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - raise NotImplementedError() - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a LATERAL alias of this :class:`_expression.Selectable`. - - The return value is the :class:`_expression.Lateral` construct also - provided by the top-level :func:`_expression.lateral` function. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - return Lateral._construct(self, name=name) - - @util.deprecated( - "1.4", - message="The :meth:`.Selectable.replace_selectable` method is " - "deprecated, and will be removed in a future release. Similar " - "functionality is available via the sqlalchemy.sql.visitors module.", - ) - @util.preload_module("sqlalchemy.sql.util") - def replace_selectable(self, old: FromClause, alias: Alias) -> Self: - """Replace all occurrences of :class:`_expression.FromClause` - 'old' with the given :class:`_expression.Alias` - object, returning a copy of this :class:`_expression.FromClause`. - - """ - return util.preloaded.sql_util.ClauseAdapter(alias).traverse(self) - - def corresponding_column( - self, column: KeyedColumnElement[Any], require_embedded: bool = False - ) -> Optional[KeyedColumnElement[Any]]: - """Given a :class:`_expression.ColumnElement`, return the exported - :class:`_expression.ColumnElement` object from the - :attr:`_expression.Selectable.exported_columns` - collection of this :class:`_expression.Selectable` - which corresponds to that - original :class:`_expression.ColumnElement` via a common ancestor - column. - - :param column: the target :class:`_expression.ColumnElement` - to be matched. - - :param require_embedded: only return corresponding columns for - the given :class:`_expression.ColumnElement`, if the given - :class:`_expression.ColumnElement` - is actually present within a sub-element - of this :class:`_expression.Selectable`. - Normally the column will match if - it merely shares a common ancestor with one of the exported - columns of this :class:`_expression.Selectable`. - - .. seealso:: - - :attr:`_expression.Selectable.exported_columns` - the - :class:`_expression.ColumnCollection` - that is used for the operation. - - :meth:`_expression.ColumnCollection.corresponding_column` - - implementation - method. - - """ - - return self.exported_columns.corresponding_column( - column, require_embedded - ) - - -class HasPrefixes: - _prefixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = () - - _has_prefixes_traverse_internals: _TraverseInternalsType = [ - ("_prefixes", InternalTraversal.dp_prefix_sequence) - ] - - @_generative - @_document_text_coercion( - "prefixes", - ":meth:`_expression.HasPrefixes.prefix_with`", - ":paramref:`.HasPrefixes.prefix_with.*prefixes`", - ) - def prefix_with( - self, - *prefixes: _TextCoercedExpressionArgument[Any], - dialect: str = "*", - ) -> Self: - r"""Add one or more expressions following the statement keyword, i.e. - SELECT, INSERT, UPDATE, or DELETE. Generative. - - This is used to support backend-specific prefix keywords such as those - provided by MySQL. - - E.g.:: - - stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql") - - # MySQL 5.7 optimizer hints - stmt = select(table).prefix_with( - "/*+ BKA(t1) */", dialect="mysql") - - Multiple prefixes can be specified by multiple calls - to :meth:`_expression.HasPrefixes.prefix_with`. - - :param \*prefixes: textual or :class:`_expression.ClauseElement` - construct which - will be rendered following the INSERT, UPDATE, or DELETE - keyword. - :param dialect: optional string dialect name which will - limit rendering of this prefix to only that dialect. - - """ - self._prefixes = self._prefixes + tuple( - [ - (coercions.expect(roles.StatementOptionRole, p), dialect) - for p in prefixes - ] - ) - return self - - -class HasSuffixes: - _suffixes: Tuple[Tuple[DQLDMLClauseElement, str], ...] = () - - _has_suffixes_traverse_internals: _TraverseInternalsType = [ - ("_suffixes", InternalTraversal.dp_prefix_sequence) - ] - - @_generative - @_document_text_coercion( - "suffixes", - ":meth:`_expression.HasSuffixes.suffix_with`", - ":paramref:`.HasSuffixes.suffix_with.*suffixes`", - ) - def suffix_with( - self, - *suffixes: _TextCoercedExpressionArgument[Any], - dialect: str = "*", - ) -> Self: - r"""Add one or more expressions following the statement as a whole. - - This is used to support backend-specific suffix keywords on - certain constructs. - - E.g.:: - - stmt = select(col1, col2).cte().suffix_with( - "cycle empno set y_cycle to 1 default 0", dialect="oracle") - - Multiple suffixes can be specified by multiple calls - to :meth:`_expression.HasSuffixes.suffix_with`. - - :param \*suffixes: textual or :class:`_expression.ClauseElement` - construct which - will be rendered following the target clause. - :param dialect: Optional string dialect name which will - limit rendering of this suffix to only that dialect. - - """ - self._suffixes = self._suffixes + tuple( - [ - (coercions.expect(roles.StatementOptionRole, p), dialect) - for p in suffixes - ] - ) - return self - - -class HasHints: - _hints: util.immutabledict[Tuple[FromClause, str], str] = ( - util.immutabledict() - ) - _statement_hints: Tuple[Tuple[str, str], ...] = () - - _has_hints_traverse_internals: _TraverseInternalsType = [ - ("_statement_hints", InternalTraversal.dp_statement_hint_list), - ("_hints", InternalTraversal.dp_table_hint_list), - ] - - def with_statement_hint(self, text: str, dialect_name: str = "*") -> Self: - """Add a statement hint to this :class:`_expression.Select` or - other selectable object. - - This method is similar to :meth:`_expression.Select.with_hint` - except that - it does not require an individual table, and instead applies to the - statement as a whole. - - Hints here are specific to the backend database and may include - directives such as isolation levels, file directives, fetch directives, - etc. - - .. seealso:: - - :meth:`_expression.Select.with_hint` - - :meth:`_expression.Select.prefix_with` - generic SELECT prefixing - which also can suit some database-specific HINT syntaxes such as - MySQL optimizer hints - - """ - return self._with_hint(None, text, dialect_name) - - @_generative - def with_hint( - self, - selectable: _FromClauseArgument, - text: str, - dialect_name: str = "*", - ) -> Self: - r"""Add an indexing or other executional context hint for the given - selectable to this :class:`_expression.Select` or other selectable - object. - - The text of the hint is rendered in the appropriate - location for the database backend in use, relative - to the given :class:`_schema.Table` or :class:`_expression.Alias` - passed as the - ``selectable`` argument. The dialect implementation - typically uses Python string substitution syntax - with the token ``%(name)s`` to render the name of - the table or alias. E.g. when using Oracle, the - following:: - - select(mytable).\ - with_hint(mytable, "index(%(name)s ix_mytable)") - - Would render SQL as:: - - select /*+ index(mytable ix_mytable) */ ... from mytable - - The ``dialect_name`` option will limit the rendering of a particular - hint to a particular backend. Such as, to add hints for both Oracle - and Sybase simultaneously:: - - select(mytable).\ - with_hint(mytable, "index(%(name)s ix_mytable)", 'oracle').\ - with_hint(mytable, "WITH INDEX ix_mytable", 'mssql') - - .. seealso:: - - :meth:`_expression.Select.with_statement_hint` - - """ - - return self._with_hint(selectable, text, dialect_name) - - def _with_hint( - self, - selectable: Optional[_FromClauseArgument], - text: str, - dialect_name: str, - ) -> Self: - if selectable is None: - self._statement_hints += ((dialect_name, text),) - else: - self._hints = self._hints.union( - { - ( - coercions.expect(roles.FromClauseRole, selectable), - dialect_name, - ): text - } - ) - return self - - -class FromClause(roles.AnonymizedFromClauseRole, Selectable): - """Represent an element that can be used within the ``FROM`` - clause of a ``SELECT`` statement. - - The most common forms of :class:`_expression.FromClause` are the - :class:`_schema.Table` and the :func:`_expression.select` constructs. Key - features common to all :class:`_expression.FromClause` objects include: - - * a :attr:`.c` collection, which provides per-name access to a collection - of :class:`_expression.ColumnElement` objects. - * a :attr:`.primary_key` attribute, which is a collection of all those - :class:`_expression.ColumnElement` - objects that indicate the ``primary_key`` flag. - * Methods to generate various derivations of a "from" clause, including - :meth:`_expression.FromClause.alias`, - :meth:`_expression.FromClause.join`, - :meth:`_expression.FromClause.select`. - - - """ - - __visit_name__ = "fromclause" - named_with_column = False - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return () - - _is_clone_of: Optional[FromClause] - - _columns: ColumnCollection[Any, Any] - - schema: Optional[str] = None - """Define the 'schema' attribute for this :class:`_expression.FromClause`. - - This is typically ``None`` for most objects except that of - :class:`_schema.Table`, where it is taken as the value of the - :paramref:`_schema.Table.schema` argument. - - """ - - is_selectable = True - _is_from_clause = True - _is_join = False - - _use_schema_map = False - - def select(self) -> Select[Any]: - r"""Return a SELECT of this :class:`_expression.FromClause`. - - - e.g.:: - - stmt = some_table.select().where(some_table.c.id == 5) - - .. seealso:: - - :func:`_expression.select` - general purpose - method which allows for arbitrary column lists. - - """ - return Select(self) - - def join( - self, - right: _FromClauseArgument, - onclause: Optional[_ColumnExpressionArgument[bool]] = None, - isouter: bool = False, - full: bool = False, - ) -> Join: - """Return a :class:`_expression.Join` from this - :class:`_expression.FromClause` - to another :class:`FromClause`. - - E.g.:: - - from sqlalchemy import join - - j = user_table.join(address_table, - user_table.c.id == address_table.c.user_id) - stmt = select(user_table).select_from(j) - - would emit SQL along the lines of:: - - SELECT user.id, user.name FROM user - JOIN address ON user.id = address.user_id - - :param right: the right side of the join; this is any - :class:`_expression.FromClause` object such as a - :class:`_schema.Table` object, and - may also be a selectable-compatible object such as an ORM-mapped - class. - - :param onclause: a SQL expression representing the ON clause of the - join. If left at ``None``, :meth:`_expression.FromClause.join` - will attempt to - join the two tables based on a foreign key relationship. - - :param isouter: if True, render a LEFT OUTER JOIN, instead of JOIN. - - :param full: if True, render a FULL OUTER JOIN, instead of LEFT OUTER - JOIN. Implies :paramref:`.FromClause.join.isouter`. - - .. seealso:: - - :func:`_expression.join` - standalone function - - :class:`_expression.Join` - the type of object produced - - """ - - return Join(self, right, onclause, isouter, full) - - def outerjoin( - self, - right: _FromClauseArgument, - onclause: Optional[_ColumnExpressionArgument[bool]] = None, - full: bool = False, - ) -> Join: - """Return a :class:`_expression.Join` from this - :class:`_expression.FromClause` - to another :class:`FromClause`, with the "isouter" flag set to - True. - - E.g.:: - - from sqlalchemy import outerjoin - - j = user_table.outerjoin(address_table, - user_table.c.id == address_table.c.user_id) - - The above is equivalent to:: - - j = user_table.join( - address_table, - user_table.c.id == address_table.c.user_id, - isouter=True) - - :param right: the right side of the join; this is any - :class:`_expression.FromClause` object such as a - :class:`_schema.Table` object, and - may also be a selectable-compatible object such as an ORM-mapped - class. - - :param onclause: a SQL expression representing the ON clause of the - join. If left at ``None``, :meth:`_expression.FromClause.join` - will attempt to - join the two tables based on a foreign key relationship. - - :param full: if True, render a FULL OUTER JOIN, instead of - LEFT OUTER JOIN. - - .. seealso:: - - :meth:`_expression.FromClause.join` - - :class:`_expression.Join` - - """ - - return Join(self, right, onclause, True, full) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> NamedFromClause: - """Return an alias of this :class:`_expression.FromClause`. - - E.g.:: - - a2 = some_table.alias('a2') - - The above code creates an :class:`_expression.Alias` - object which can be used - as a FROM clause in any SELECT statement. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - - return Alias._construct(self, name=name) - - def tablesample( - self, - sampling: Union[float, Function[Any]], - name: Optional[str] = None, - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> TableSample: - """Return a TABLESAMPLE alias of this :class:`_expression.FromClause`. - - The return value is the :class:`_expression.TableSample` - construct also - provided by the top-level :func:`_expression.tablesample` function. - - .. seealso:: - - :func:`_expression.tablesample` - usage guidelines and parameters - - """ - return TableSample._construct( - self, sampling=sampling, name=name, seed=seed - ) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - """Return ``True`` if this :class:`_expression.FromClause` is - 'derived' from the given ``FromClause``. - - An example would be an Alias of a Table is derived from that Table. - - """ - # this is essentially an "identity" check in the base class. - # Other constructs override this to traverse through - # contained elements. - return fromclause in self._cloned_set - - def _is_lexical_equivalent(self, other: FromClause) -> bool: - """Return ``True`` if this :class:`_expression.FromClause` and - the other represent the same lexical identity. - - This tests if either one is a copy of the other, or - if they are the same via annotation identity. - - """ - return bool(self._cloned_set.intersection(other._cloned_set)) - - @util.ro_non_memoized_property - def description(self) -> str: - """A brief description of this :class:`_expression.FromClause`. - - Used primarily for error message formatting. - - """ - return getattr(self, "name", self.__class__.__name__ + " object") - - def _generate_fromclause_column_proxies( - self, fromclause: FromClause - ) -> None: - fromclause._columns._populate_separate_keys( - col._make_proxy(fromclause) for col in self.c - ) - - @util.ro_non_memoized_property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.Selectable`. - - The "exported" columns for a :class:`_expression.FromClause` - object are synonymous - with the :attr:`_expression.FromClause.columns` collection. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.Selectable.exported_columns` - - :attr:`_expression.SelectBase.exported_columns` - - - """ - return self.c - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """A named-based collection of :class:`_expression.ColumnElement` - objects maintained by this :class:`_expression.FromClause`. - - The :attr:`.columns`, or :attr:`.c` collection, is the gateway - to the construction of SQL expressions using table-bound or - other selectable-bound columns:: - - select(mytable).where(mytable.c.somecolumn == 5) - - :return: a :class:`.ColumnCollection` object. - - """ - return self.c - - @util.ro_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """ - A synonym for :attr:`.FromClause.columns` - - :return: a :class:`.ColumnCollection` - - """ - if "_columns" not in self.__dict__: - self._init_collections() - self._populate_column_collection() - return self._columns.as_readonly() - - @util.ro_non_memoized_property - def entity_namespace(self) -> _EntityNamespace: - """Return a namespace used for name-based access in SQL expressions. - - This is the namespace that is used to resolve "filter_by()" type - expressions, such as:: - - stmt.filter_by(address='some address') - - It defaults to the ``.c`` collection, however internally it can - be overridden using the "entity_namespace" annotation to deliver - alternative results. - - """ - return self.c - - @util.ro_memoized_property - def primary_key(self) -> Iterable[NamedColumn[Any]]: - """Return the iterable collection of :class:`_schema.Column` objects - which comprise the primary key of this :class:`_selectable.FromClause`. - - For a :class:`_schema.Table` object, this collection is represented - by the :class:`_schema.PrimaryKeyConstraint` which itself is an - iterable collection of :class:`_schema.Column` objects. - - """ - self._init_collections() - self._populate_column_collection() - return self.primary_key - - @util.ro_memoized_property - def foreign_keys(self) -> Iterable[ForeignKey]: - """Return the collection of :class:`_schema.ForeignKey` marker objects - which this FromClause references. - - Each :class:`_schema.ForeignKey` is a member of a - :class:`_schema.Table`-wide - :class:`_schema.ForeignKeyConstraint`. - - .. seealso:: - - :attr:`_schema.Table.foreign_key_constraints` - - """ - self._init_collections() - self._populate_column_collection() - return self.foreign_keys - - def _reset_column_collection(self) -> None: - """Reset the attributes linked to the ``FromClause.c`` attribute. - - This collection is separate from all the other memoized things - as it has shown to be sensitive to being cleared out in situations - where enclosing code, typically in a replacement traversal scenario, - has already established strong relationships - with the exported columns. - - The collection is cleared for the case where a table is having a - column added to it as well as within a Join during copy internals. - - """ - - for key in ["_columns", "columns", "c", "primary_key", "foreign_keys"]: - self.__dict__.pop(key, None) - - @util.ro_non_memoized_property - def _select_iterable(self) -> _SelectIterable: - return (c for c in self.c if not _never_select_column(c)) - - def _init_collections(self) -> None: - assert "_columns" not in self.__dict__ - assert "primary_key" not in self.__dict__ - assert "foreign_keys" not in self.__dict__ - - self._columns = ColumnCollection() - self.primary_key = ColumnSet() # type: ignore - self.foreign_keys = set() # type: ignore - - @property - def _cols_populated(self) -> bool: - return "_columns" in self.__dict__ - - def _populate_column_collection(self) -> None: - """Called on subclasses to establish the .c collection. - - Each implementation has a different way of establishing - this collection. - - """ - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - """Given a column added to the .c collection of an underlying - selectable, produce the local version of that column, assuming this - selectable ultimately should proxy this column. - - this is used to "ping" a derived selectable to add a new column - to its .c. collection when a Column has been added to one of the - Table objects it ultimately derives from. - - If the given selectable hasn't populated its .c. collection yet, - it should at least pass on the message to the contained selectables, - but it will return None. - - This method is currently used by Declarative to allow Table - columns to be added to a partially constructed inheritance - mapping that may have already produced joins. The method - isn't public right now, as the full span of implications - and/or caveats aren't yet clear. - - It's also possible that this functionality could be invoked by - default via an event, which would require that - selectables maintain a weak referencing collection of all - derivations. - - """ - self._reset_column_collection() - - def _anonymous_fromclause( - self, *, name: Optional[str] = None, flat: bool = False - ) -> FromClause: - return self.alias(name=name) - - if TYPE_CHECKING: - - def self_group( - self, against: Optional[OperatorType] = None - ) -> Union[FromGrouping, Self]: ... - - -class NamedFromClause(FromClause): - """A :class:`.FromClause` that has a name. - - Examples include tables, subqueries, CTEs, aliased tables. - - .. versionadded:: 2.0 - - """ - - named_with_column = True - - name: str - - @util.preload_module("sqlalchemy.sql.sqltypes") - def table_valued(self) -> TableValuedColumn[Any]: - """Return a :class:`_sql.TableValuedColumn` object for this - :class:`_expression.FromClause`. - - A :class:`_sql.TableValuedColumn` is a :class:`_sql.ColumnElement` that - represents a complete row in a table. Support for this construct is - backend dependent, and is supported in various forms by backends - such as PostgreSQL, Oracle and SQL Server. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import select, column, func, table - >>> a = table("a", column("id"), column("x"), column("y")) - >>> stmt = select(func.row_to_json(a.table_valued())) - >>> print(stmt) - {printsql}SELECT row_to_json(a) AS row_to_json_1 - FROM a - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :ref:`tutorial_functions` - in the :ref:`unified_tutorial` - - """ - return TableValuedColumn(self, type_api.TABLEVALUE) - - -class SelectLabelStyle(Enum): - """Label style constants that may be passed to - :meth:`_sql.Select.set_label_style`.""" - - LABEL_STYLE_NONE = 0 - """Label style indicating no automatic labeling should be applied to the - columns clause of a SELECT statement. - - Below, the columns named ``columna`` are both rendered as is, meaning that - the name ``columna`` can only refer to the first occurrence of this name - within a result set, as well as if the statement were used as a subquery: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_NONE - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_NONE)) - {printsql}SELECT table1.columna, table1.columnb, table2.columna, table2.columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.Select.set_label_style` method. - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_TABLENAME_PLUS_COL = 1 - """Label style indicating all columns should be labeled as - ``<tablename>_<columnname>`` when generating the columns clause of a SELECT - statement, to disambiguate same-named columns referenced from different - tables, aliases, or subqueries. - - Below, all column names are given a label so that the two same-named - columns ``columna`` are disambiguated as ``table1_columna`` and - ``table2_columna``: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_TABLENAME_PLUS_COL - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)) - {printsql}SELECT table1.columna AS table1_columna, table1.columnb AS table1_columnb, table2.columna AS table2_columna, table2.columnc AS table2_columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.GenerativeSelect.set_label_style` method. - Equivalent to the legacy method ``Select.apply_labels()``; - :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` is SQLAlchemy's legacy - auto-labeling style. :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` provides a - less intrusive approach to disambiguation of same-named column expressions. - - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_DISAMBIGUATE_ONLY = 2 - """Label style indicating that columns with a name that conflicts with - an existing name should be labeled with a semi-anonymizing label - when generating the columns clause of a SELECT statement. - - Below, most column names are left unaffected, except for the second - occurrence of the name ``columna``, which is labeled using the - label ``columna_1`` to disambiguate it from that of ``tablea.columna``: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import table, column, select, true, LABEL_STYLE_DISAMBIGUATE_ONLY - >>> table1 = table("table1", column("columna"), column("columnb")) - >>> table2 = table("table2", column("columna"), column("columnc")) - >>> print(select(table1, table2).join(table2, true()).set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY)) - {printsql}SELECT table1.columna, table1.columnb, table2.columna AS columna_1, table2.columnc - FROM table1 JOIN table2 ON true - - Used with the :meth:`_sql.GenerativeSelect.set_label_style` method, - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` is the default labeling style - for all SELECT statements outside of :term:`1.x style` ORM queries. - - .. versionadded:: 1.4 - - """ # noqa: E501 - - LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY - """The default label style, refers to - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`. - - .. versionadded:: 1.4 - - """ - - LABEL_STYLE_LEGACY_ORM = 3 - - -( - LABEL_STYLE_NONE, - LABEL_STYLE_TABLENAME_PLUS_COL, - LABEL_STYLE_DISAMBIGUATE_ONLY, - _, -) = list(SelectLabelStyle) - -LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY - - -class Join(roles.DMLTableRole, FromClause): - """Represent a ``JOIN`` construct between two - :class:`_expression.FromClause` - elements. - - The public constructor function for :class:`_expression.Join` - is the module-level - :func:`_expression.join()` function, as well as the - :meth:`_expression.FromClause.join` method - of any :class:`_expression.FromClause` (e.g. such as - :class:`_schema.Table`). - - .. seealso:: - - :func:`_expression.join` - - :meth:`_expression.FromClause.join` - - """ - - __visit_name__ = "join" - - _traverse_internals: _TraverseInternalsType = [ - ("left", InternalTraversal.dp_clauseelement), - ("right", InternalTraversal.dp_clauseelement), - ("onclause", InternalTraversal.dp_clauseelement), - ("isouter", InternalTraversal.dp_boolean), - ("full", InternalTraversal.dp_boolean), - ] - - _is_join = True - - left: FromClause - right: FromClause - onclause: Optional[ColumnElement[bool]] - isouter: bool - full: bool - - def __init__( - self, - left: _FromClauseArgument, - right: _FromClauseArgument, - onclause: Optional[_OnClauseArgument] = None, - isouter: bool = False, - full: bool = False, - ): - """Construct a new :class:`_expression.Join`. - - The usual entrypoint here is the :func:`_expression.join` - function or the :meth:`_expression.FromClause.join` method of any - :class:`_expression.FromClause` object. - - """ - - # when deannotate was removed here, callcounts went up for ORM - # compilation of eager joins, since there were more comparisons of - # annotated objects. test_orm.py -> test_fetch_results - # was therefore changed to show a more real-world use case, where the - # compilation is cached; there's no change in post-cache callcounts. - # callcounts for a single compilation in that particular test - # that includes about eight joins about 1100 extra fn calls, from - # 29200 -> 30373 - - self.left = coercions.expect( - roles.FromClauseRole, - left, - ) - self.right = coercions.expect( - roles.FromClauseRole, - right, - ).self_group() - - if onclause is None: - self.onclause = self._match_primaries(self.left, self.right) - else: - # note: taken from If91f61527236fd4d7ae3cad1f24c38be921c90ba - # not merged yet - self.onclause = coercions.expect( - roles.OnClauseRole, onclause - ).self_group(against=operators._asbool) - - self.isouter = isouter - self.full = full - - @util.ro_non_memoized_property - def description(self) -> str: - return "Join object on %s(%d) and %s(%d)" % ( - self.left.description, - id(self.left), - self.right.description, - id(self.right), - ) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - return ( - # use hash() to ensure direct comparison to annotated works - # as well - hash(fromclause) == hash(self) - or self.left.is_derived_from(fromclause) - or self.right.is_derived_from(fromclause) - ) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> FromGrouping: - ... - return FromGrouping(self) - - @util.preload_module("sqlalchemy.sql.util") - def _populate_column_collection(self) -> None: - sqlutil = util.preloaded.sql_util - columns: List[KeyedColumnElement[Any]] = [c for c in self.left.c] + [ - c for c in self.right.c - ] - - self.primary_key.extend( # type: ignore - sqlutil.reduce_columns( - (c for c in columns if c.primary_key), self.onclause - ) - ) - self._columns._populate_separate_keys( - (col._tq_key_label, col) for col in columns - ) - self.foreign_keys.update( # type: ignore - itertools.chain(*[col.foreign_keys for col in columns]) - ) - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - # see Select._copy_internals() for similar concept - - # here we pre-clone "left" and "right" so that we can - # determine the new FROM clauses - all_the_froms = set( - itertools.chain( - _from_objects(self.left), - _from_objects(self.right), - ) - ) - - # run the clone on those. these will be placed in the - # cache used by the clone function - new_froms = {f: clone(f, **kw) for f in all_the_froms} - - # set up a special replace function that will replace for - # ColumnClause with parent table referring to those - # replaced FromClause objects - def replace( - obj: Union[BinaryExpression[Any], ColumnClause[Any]], - **kw: Any, - ) -> Optional[KeyedColumnElement[ColumnElement[Any]]]: - if isinstance(obj, ColumnClause) and obj.table in new_froms: - newelem = new_froms[obj.table].corresponding_column(obj) - return newelem - return None - - kw["replace"] = replace - - # run normal _copy_internals. the clones for - # left and right will come from the clone function's - # cache - super()._copy_internals(clone=clone, **kw) - - self._reset_memoizations() - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - self.left._refresh_for_new_column(column) - self.right._refresh_for_new_column(column) - - def _match_primaries( - self, - left: FromClause, - right: FromClause, - ) -> ColumnElement[bool]: - if isinstance(left, Join): - left_right = left.right - else: - left_right = None - return self._join_condition(left, right, a_subset=left_right) - - @classmethod - def _join_condition( - cls, - a: FromClause, - b: FromClause, - *, - a_subset: Optional[FromClause] = None, - consider_as_foreign_keys: Optional[ - AbstractSet[ColumnClause[Any]] - ] = None, - ) -> ColumnElement[bool]: - """Create a join condition between two tables or selectables. - - See sqlalchemy.sql.util.join_condition() for full docs. - - """ - constraints = cls._joincond_scan_left_right( - a, a_subset, b, consider_as_foreign_keys - ) - - if len(constraints) > 1: - cls._joincond_trim_constraints( - a, b, constraints, consider_as_foreign_keys - ) - - if len(constraints) == 0: - if isinstance(b, FromGrouping): - hint = ( - " Perhaps you meant to convert the right side to a " - "subquery using alias()?" - ) - else: - hint = "" - raise exc.NoForeignKeysError( - "Can't find any foreign key relationships " - "between '%s' and '%s'.%s" - % (a.description, b.description, hint) - ) - - crit = [(x == y) for x, y in list(constraints.values())[0]] - if len(crit) == 1: - return crit[0] - else: - return and_(*crit) - - @classmethod - def _can_join( - cls, - left: FromClause, - right: FromClause, - *, - consider_as_foreign_keys: Optional[ - AbstractSet[ColumnClause[Any]] - ] = None, - ) -> bool: - if isinstance(left, Join): - left_right = left.right - else: - left_right = None - - constraints = cls._joincond_scan_left_right( - a=left, - b=right, - a_subset=left_right, - consider_as_foreign_keys=consider_as_foreign_keys, - ) - - return bool(constraints) - - @classmethod - @util.preload_module("sqlalchemy.sql.util") - def _joincond_scan_left_right( - cls, - a: FromClause, - a_subset: Optional[FromClause], - b: FromClause, - consider_as_foreign_keys: Optional[AbstractSet[ColumnClause[Any]]], - ) -> collections.defaultdict[ - Optional[ForeignKeyConstraint], - List[Tuple[ColumnClause[Any], ColumnClause[Any]]], - ]: - sql_util = util.preloaded.sql_util - - a = coercions.expect(roles.FromClauseRole, a) - b = coercions.expect(roles.FromClauseRole, b) - - constraints: collections.defaultdict[ - Optional[ForeignKeyConstraint], - List[Tuple[ColumnClause[Any], ColumnClause[Any]]], - ] = collections.defaultdict(list) - - for left in (a_subset, a): - if left is None: - continue - for fk in sorted( - b.foreign_keys, - key=lambda fk: fk.parent._creation_order, - ): - if ( - consider_as_foreign_keys is not None - and fk.parent not in consider_as_foreign_keys - ): - continue - try: - col = fk.get_referent(left) - except exc.NoReferenceError as nrte: - table_names = {t.name for t in sql_util.find_tables(left)} - if nrte.table_name in table_names: - raise - else: - continue - - if col is not None: - constraints[fk.constraint].append((col, fk.parent)) - if left is not b: - for fk in sorted( - left.foreign_keys, - key=lambda fk: fk.parent._creation_order, - ): - if ( - consider_as_foreign_keys is not None - and fk.parent not in consider_as_foreign_keys - ): - continue - try: - col = fk.get_referent(b) - except exc.NoReferenceError as nrte: - table_names = {t.name for t in sql_util.find_tables(b)} - if nrte.table_name in table_names: - raise - else: - continue - - if col is not None: - constraints[fk.constraint].append((col, fk.parent)) - if constraints: - break - return constraints - - @classmethod - def _joincond_trim_constraints( - cls, - a: FromClause, - b: FromClause, - constraints: Dict[Any, Any], - consider_as_foreign_keys: Optional[Any], - ) -> None: - # more than one constraint matched. narrow down the list - # to include just those FKCs that match exactly to - # "consider_as_foreign_keys". - if consider_as_foreign_keys: - for const in list(constraints): - if {f.parent for f in const.elements} != set( - consider_as_foreign_keys - ): - del constraints[const] - - # if still multiple constraints, but - # they all refer to the exact same end result, use it. - if len(constraints) > 1: - dedupe = {tuple(crit) for crit in constraints.values()} - if len(dedupe) == 1: - key = list(constraints)[0] - constraints = {key: constraints[key]} - - if len(constraints) != 1: - raise exc.AmbiguousForeignKeysError( - "Can't determine join between '%s' and '%s'; " - "tables have more than one foreign key " - "constraint relationship between them. " - "Please specify the 'onclause' of this " - "join explicitly." % (a.description, b.description) - ) - - def select(self) -> Select[Any]: - r"""Create a :class:`_expression.Select` from this - :class:`_expression.Join`. - - E.g.:: - - stmt = table_a.join(table_b, table_a.c.id == table_b.c.a_id) - - stmt = stmt.select() - - The above will produce a SQL string resembling:: - - SELECT table_a.id, table_a.col, table_b.id, table_b.a_id - FROM table_a JOIN table_b ON table_a.id = table_b.a_id - - """ - return Select(self.left, self.right).select_from(self) - - @util.preload_module("sqlalchemy.sql.util") - def _anonymous_fromclause( - self, name: Optional[str] = None, flat: bool = False - ) -> TODO_Any: - sqlutil = util.preloaded.sql_util - if flat: - if name is not None: - raise exc.ArgumentError("Can't send name argument with flat") - left_a, right_a = ( - self.left._anonymous_fromclause(flat=True), - self.right._anonymous_fromclause(flat=True), - ) - adapter = sqlutil.ClauseAdapter(left_a).chain( - sqlutil.ClauseAdapter(right_a) - ) - - return left_a.join( - right_a, - adapter.traverse(self.onclause), - isouter=self.isouter, - full=self.full, - ) - else: - return ( - self.select() - .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) - .correlate(None) - .alias(name) - ) - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return itertools.chain( - *[_from_objects(x.left, x.right) for x in self._cloned_set] - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - self_list: List[FromClause] = [self] - return self_list + self.left._from_objects + self.right._from_objects - - -class NoInit: - def __init__(self, *arg: Any, **kw: Any): - raise NotImplementedError( - "The %s class is not intended to be constructed " - "directly. Please use the %s() standalone " - "function or the %s() method available from appropriate " - "selectable objects." - % ( - self.__class__.__name__, - self.__class__.__name__.lower(), - self.__class__.__name__.lower(), - ) - ) - - -class LateralFromClause(NamedFromClause): - """mark a FROM clause as being able to render directly as LATERAL""" - - -# FromClause -> -# AliasedReturnsRows -# -> Alias only for FromClause -# -> Subquery only for SelectBase -# -> CTE only for HasCTE -> SelectBase, DML -# -> Lateral -> FromClause, but we accept SelectBase -# w/ non-deprecated coercion -# -> TableSample -> only for FromClause - - -class AliasedReturnsRows(NoInit, NamedFromClause): - """Base class of aliases against tables, subqueries, and other - selectables.""" - - _is_from_container = True - - _supports_derived_columns = False - - element: ReturnsRows - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("name", InternalTraversal.dp_anon_name), - ] - - @classmethod - def _construct( - cls, - selectable: Any, - *, - name: Optional[str] = None, - **kw: Any, - ) -> Self: - obj = cls.__new__(cls) - obj._init(selectable, name=name, **kw) - return obj - - def _init(self, selectable: Any, *, name: Optional[str] = None) -> None: - self.element = coercions.expect( - roles.ReturnsRowsRole, selectable, apply_propagate_attrs=self - ) - self.element = selectable - self._orig_name = name - if name is None: - if ( - isinstance(selectable, FromClause) - and selectable.named_with_column - ): - name = getattr(selectable, "name", None) - if isinstance(name, _anonymous_label): - name = None - name = _anonymous_label.safe_construct(id(self), name or "anon") - self.name = name - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - self.element._refresh_for_new_column(column) - - def _populate_column_collection(self) -> None: - self.element._generate_fromclause_column_proxies(self) - - @util.ro_non_memoized_property - def description(self) -> str: - name = self.name - if isinstance(name, _anonymous_label): - name = "anon_1" - - return name - - @util.ro_non_memoized_property - def implicit_returning(self) -> bool: - return self.element.implicit_returning # type: ignore - - @property - def original(self) -> ReturnsRows: - """Legacy for dialects that are referring to Alias.original.""" - return self.element - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - if fromclause in self._cloned_set: - return True - return self.element.is_derived_from(fromclause) - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - existing_element = self.element - - super()._copy_internals(clone=clone, **kw) - - # the element clone is usually against a Table that returns the - # same object. don't reset exported .c. collections and other - # memoized details if it was not changed. this saves a lot on - # performance. - if existing_element is not self.element: - self._reset_column_collection() - - @property - def _from_objects(self) -> List[FromClause]: - return [self] - - -class FromClauseAlias(AliasedReturnsRows): - element: FromClause - - -class Alias(roles.DMLTableRole, FromClauseAlias): - """Represents an table or selectable alias (AS). - - Represents an alias, as typically applied to any table or - sub-select within a SQL statement using the ``AS`` keyword (or - without the keyword on certain databases such as Oracle). - - This object is constructed from the :func:`_expression.alias` module - level function as well as the :meth:`_expression.FromClause.alias` - method available - on all :class:`_expression.FromClause` subclasses. - - .. seealso:: - - :meth:`_expression.FromClause.alias` - - """ - - __visit_name__ = "alias" - - inherit_cache = True - - element: FromClause - - @classmethod - def _factory( - cls, - selectable: FromClause, - name: Optional[str] = None, - flat: bool = False, - ) -> NamedFromClause: - return coercions.expect( - roles.FromClauseRole, selectable, allow_select=True - ).alias(name=name, flat=flat) - - -class TableValuedAlias(LateralFromClause, Alias): - """An alias against a "table valued" SQL function. - - This construct provides for a SQL function that returns columns - to be used in the FROM clause of a SELECT statement. The - object is generated using the :meth:`_functions.FunctionElement.table_valued` - method, e.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import select, func - >>> fn = func.json_array_elements_text('["one", "two", "three"]').table_valued("value") - >>> print(select(fn.c.value)) - {printsql}SELECT anon_1.value - FROM json_array_elements_text(:json_array_elements_text_1) AS anon_1 - - .. versionadded:: 1.4.0b2 - - .. seealso:: - - :ref:`tutorial_functions_table_valued` - in the :ref:`unified_tutorial` - - """ # noqa: E501 - - __visit_name__ = "table_valued_alias" - - _supports_derived_columns = True - _render_derived = False - _render_derived_w_types = False - joins_implicitly = False - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("name", InternalTraversal.dp_anon_name), - ("_tableval_type", InternalTraversal.dp_type), - ("_render_derived", InternalTraversal.dp_boolean), - ("_render_derived_w_types", InternalTraversal.dp_boolean), - ] - - def _init( - self, - selectable: Any, - *, - name: Optional[str] = None, - table_value_type: Optional[TableValueType] = None, - joins_implicitly: bool = False, - ) -> None: - super()._init(selectable, name=name) - - self.joins_implicitly = joins_implicitly - self._tableval_type = ( - type_api.TABLEVALUE - if table_value_type is None - else table_value_type - ) - - @HasMemoized.memoized_attribute - def column(self) -> TableValuedColumn[Any]: - """Return a column expression representing this - :class:`_sql.TableValuedAlias`. - - This accessor is used to implement the - :meth:`_functions.FunctionElement.column_valued` method. See that - method for further details. - - E.g.: - - .. sourcecode:: pycon+sql - - >>> print(select(func.some_func().table_valued("value").column)) - {printsql}SELECT anon_1 FROM some_func() AS anon_1 - - .. seealso:: - - :meth:`_functions.FunctionElement.column_valued` - - """ - - return TableValuedColumn(self, self._tableval_type) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> TableValuedAlias: - """Return a new alias of this :class:`_sql.TableValuedAlias`. - - This creates a distinct FROM object that will be distinguished - from the original one when used in a SQL statement. - - """ - - tva: TableValuedAlias = TableValuedAlias._construct( - self, - name=name, - table_value_type=self._tableval_type, - joins_implicitly=self.joins_implicitly, - ) - - if self._render_derived: - tva._render_derived = True - tva._render_derived_w_types = self._render_derived_w_types - - return tva - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a new :class:`_sql.TableValuedAlias` with the lateral flag - set, so that it renders as LATERAL. - - .. seealso:: - - :func:`_expression.lateral` - - """ - tva = self.alias(name=name) - tva._is_lateral = True - return tva - - def render_derived( - self, - name: Optional[str] = None, - with_types: bool = False, - ) -> TableValuedAlias: - """Apply "render derived" to this :class:`_sql.TableValuedAlias`. - - This has the effect of the individual column names listed out - after the alias name in the "AS" sequence, e.g.: - - .. sourcecode:: pycon+sql - - >>> print( - ... select( - ... func.unnest(array(["one", "two", "three"])). - table_valued("x", with_ordinality="o").render_derived() - ... ) - ... ) - {printsql}SELECT anon_1.x, anon_1.o - FROM unnest(ARRAY[%(param_1)s, %(param_2)s, %(param_3)s]) WITH ORDINALITY AS anon_1(x, o) - - The ``with_types`` keyword will render column types inline within - the alias expression (this syntax currently applies to the - PostgreSQL database): - - .. sourcecode:: pycon+sql - - >>> print( - ... select( - ... func.json_to_recordset( - ... '[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]' - ... ) - ... .table_valued(column("a", Integer), column("b", String)) - ... .render_derived(with_types=True) - ... ) - ... ) - {printsql}SELECT anon_1.a, anon_1.b FROM json_to_recordset(:json_to_recordset_1) - AS anon_1(a INTEGER, b VARCHAR) - - :param name: optional string name that will be applied to the alias - generated. If left as None, a unique anonymizing name will be used. - - :param with_types: if True, the derived columns will include the - datatype specification with each column. This is a special syntax - currently known to be required by PostgreSQL for some SQL functions. - - """ # noqa: E501 - - # note: don't use the @_generative system here, keep a reference - # to the original object. otherwise you can have re-use of the - # python id() of the original which can cause name conflicts if - # a new anon-name grabs the same identifier as the local anon-name - # (just saw it happen on CI) - - # construct against original to prevent memory growth - # for repeated generations - new_alias: TableValuedAlias = TableValuedAlias._construct( - self.element, - name=name, - table_value_type=self._tableval_type, - joins_implicitly=self.joins_implicitly, - ) - new_alias._render_derived = True - new_alias._render_derived_w_types = with_types - return new_alias - - -class Lateral(FromClauseAlias, LateralFromClause): - """Represent a LATERAL subquery. - - This object is constructed from the :func:`_expression.lateral` module - level function as well as the :meth:`_expression.FromClause.lateral` - method available - on all :class:`_expression.FromClause` subclasses. - - While LATERAL is part of the SQL standard, currently only more recent - PostgreSQL versions provide support for this keyword. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - - __visit_name__ = "lateral" - _is_lateral = True - - inherit_cache = True - - @classmethod - def _factory( - cls, - selectable: Union[SelectBase, _FromClauseArgument], - name: Optional[str] = None, - ) -> LateralFromClause: - return coercions.expect( - roles.FromClauseRole, selectable, explicit_subquery=True - ).lateral(name=name) - - -class TableSample(FromClauseAlias): - """Represent a TABLESAMPLE clause. - - This object is constructed from the :func:`_expression.tablesample` module - level function as well as the :meth:`_expression.FromClause.tablesample` - method - available on all :class:`_expression.FromClause` subclasses. - - .. seealso:: - - :func:`_expression.tablesample` - - """ - - __visit_name__ = "tablesample" - - _traverse_internals: _TraverseInternalsType = ( - AliasedReturnsRows._traverse_internals - + [ - ("sampling", InternalTraversal.dp_clauseelement), - ("seed", InternalTraversal.dp_clauseelement), - ] - ) - - @classmethod - def _factory( - cls, - selectable: _FromClauseArgument, - sampling: Union[float, Function[Any]], - name: Optional[str] = None, - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> TableSample: - return coercions.expect(roles.FromClauseRole, selectable).tablesample( - sampling, name=name, seed=seed - ) - - @util.preload_module("sqlalchemy.sql.functions") - def _init( # type: ignore[override] - self, - selectable: Any, - *, - name: Optional[str] = None, - sampling: Union[float, Function[Any]], - seed: Optional[roles.ExpressionElementRole[Any]] = None, - ) -> None: - assert sampling is not None - functions = util.preloaded.sql_functions - if not isinstance(sampling, functions.Function): - sampling = functions.func.system(sampling) - - self.sampling: Function[Any] = sampling - self.seed = seed - super()._init(selectable, name=name) - - def _get_method(self) -> Function[Any]: - return self.sampling - - -class CTE( - roles.DMLTableRole, - roles.IsCTERole, - Generative, - HasPrefixes, - HasSuffixes, - AliasedReturnsRows, -): - """Represent a Common Table Expression. - - The :class:`_expression.CTE` object is obtained using the - :meth:`_sql.SelectBase.cte` method from any SELECT statement. A less often - available syntax also allows use of the :meth:`_sql.HasCTE.cte` method - present on :term:`DML` constructs such as :class:`_sql.Insert`, - :class:`_sql.Update` and - :class:`_sql.Delete`. See the :meth:`_sql.HasCTE.cte` method for - usage details on CTEs. - - .. seealso:: - - :ref:`tutorial_subqueries_ctes` - in the 2.0 tutorial - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - - __visit_name__ = "cte" - - _traverse_internals: _TraverseInternalsType = ( - AliasedReturnsRows._traverse_internals - + [ - ("_cte_alias", InternalTraversal.dp_clauseelement), - ("_restates", InternalTraversal.dp_clauseelement), - ("recursive", InternalTraversal.dp_boolean), - ("nesting", InternalTraversal.dp_boolean), - ] - + HasPrefixes._has_prefixes_traverse_internals - + HasSuffixes._has_suffixes_traverse_internals - ) - - element: HasCTE - - @classmethod - def _factory( - cls, - selectable: HasCTE, - name: Optional[str] = None, - recursive: bool = False, - ) -> CTE: - r"""Return a new :class:`_expression.CTE`, - or Common Table Expression instance. - - Please see :meth:`_expression.HasCTE.cte` for detail on CTE usage. - - """ - return coercions.expect(roles.HasCTERole, selectable).cte( - name=name, recursive=recursive - ) - - def _init( - self, - selectable: Select[Any], - *, - name: Optional[str] = None, - recursive: bool = False, - nesting: bool = False, - _cte_alias: Optional[CTE] = None, - _restates: Optional[CTE] = None, - _prefixes: Optional[Tuple[()]] = None, - _suffixes: Optional[Tuple[()]] = None, - ) -> None: - self.recursive = recursive - self.nesting = nesting - self._cte_alias = _cte_alias - # Keep recursivity reference with union/union_all - self._restates = _restates - if _prefixes: - self._prefixes = _prefixes - if _suffixes: - self._suffixes = _suffixes - super()._init(selectable, name=name) - - def _populate_column_collection(self) -> None: - if self._cte_alias is not None: - self._cte_alias._generate_fromclause_column_proxies(self) - else: - self.element._generate_fromclause_column_proxies(self) - - def alias(self, name: Optional[str] = None, flat: bool = False) -> CTE: - """Return an :class:`_expression.Alias` of this - :class:`_expression.CTE`. - - This method is a CTE-specific specialization of the - :meth:`_expression.FromClause.alias` method. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - return CTE._construct( - self.element, - name=name, - recursive=self.recursive, - nesting=self.nesting, - _cte_alias=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def union(self, *other: _SelectStatementForCompoundArgument) -> CTE: - r"""Return a new :class:`_expression.CTE` with a SQL ``UNION`` - of the original CTE against the given selectables provided - as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 multiple elements are now accepted. - - .. seealso:: - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - assert is_select_statement( - self.element - ), f"CTE element f{self.element} does not support union()" - - return CTE._construct( - self.element.union(*other), - name=self.name, - recursive=self.recursive, - nesting=self.nesting, - _restates=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def union_all(self, *other: _SelectStatementForCompoundArgument) -> CTE: - r"""Return a new :class:`_expression.CTE` with a SQL ``UNION ALL`` - of the original CTE against the given selectables provided - as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 multiple elements are now accepted. - - .. seealso:: - - :meth:`_sql.HasCTE.cte` - examples of calling styles - - """ - - assert is_select_statement( - self.element - ), f"CTE element f{self.element} does not support union_all()" - - return CTE._construct( - self.element.union_all(*other), - name=self.name, - recursive=self.recursive, - nesting=self.nesting, - _restates=self, - _prefixes=self._prefixes, - _suffixes=self._suffixes, - ) - - def _get_reference_cte(self) -> CTE: - """ - A recursive CTE is updated to attach the recursive part. - Updated CTEs should still refer to the original CTE. - This function returns this reference identifier. - """ - return self._restates if self._restates is not None else self - - -class _CTEOpts(NamedTuple): - nesting: bool - - -class _ColumnsPlusNames(NamedTuple): - required_label_name: Optional[str] - """ - string label name, if non-None, must be rendered as a - label, i.e. "AS <name>" - """ - - proxy_key: Optional[str] - """ - proxy_key that is to be part of the result map for this - col. this is also the key in a fromclause.c or - select.selected_columns collection - """ - - fallback_label_name: Optional[str] - """ - name that can be used to render an "AS <name>" when - we have to render a label even though - required_label_name was not given - """ - - column: Union[ColumnElement[Any], TextClause] - """ - the ColumnElement itself - """ - - repeated: bool - """ - True if this is a duplicate of a previous column - in the list of columns - """ - - -class SelectsRows(ReturnsRows): - """Sub-base of ReturnsRows for elements that deliver rows - directly, namely SELECT and INSERT/UPDATE/DELETE..RETURNING""" - - _label_style: SelectLabelStyle = LABEL_STYLE_NONE - - def _generate_columns_plus_names( - self, - anon_for_dupe_key: bool, - cols: Optional[_SelectIterable] = None, - ) -> List[_ColumnsPlusNames]: - """Generate column names as rendered in a SELECT statement by - the compiler. - - This is distinct from the _column_naming_convention generator that's - intended for population of .c collections and similar, which has - different rules. the collection returned here calls upon the - _column_naming_convention as well. - - """ - - if cols is None: - cols = self._all_selected_columns - - key_naming_convention = SelectState._column_naming_convention( - self._label_style - ) - - names = {} - - result: List[_ColumnsPlusNames] = [] - result_append = result.append - - table_qualified = self._label_style is LABEL_STYLE_TABLENAME_PLUS_COL - label_style_none = self._label_style is LABEL_STYLE_NONE - - # a counter used for "dedupe" labels, which have double underscores - # in them and are never referred by name; they only act - # as positional placeholders. they need only be unique within - # the single columns clause they're rendered within (required by - # some dbs such as mysql). So their anon identity is tracked against - # a fixed counter rather than hash() identity. - dedupe_hash = 1 - - for c in cols: - repeated = False - - if not c._render_label_in_columns_clause: - effective_name = required_label_name = fallback_label_name = ( - None - ) - elif label_style_none: - if TYPE_CHECKING: - assert is_column_element(c) - - effective_name = required_label_name = None - fallback_label_name = c._non_anon_label or c._anon_name_label - else: - if TYPE_CHECKING: - assert is_column_element(c) - - if table_qualified: - required_label_name = effective_name = ( - fallback_label_name - ) = c._tq_label - else: - effective_name = fallback_label_name = c._non_anon_label - required_label_name = None - - if effective_name is None: - # it seems like this could be _proxy_key and we would - # not need _expression_label but it isn't - # giving us a clue when to use anon_label instead - expr_label = c._expression_label - if expr_label is None: - repeated = c._anon_name_label in names - names[c._anon_name_label] = c - effective_name = required_label_name = None - - if repeated: - # here, "required_label_name" is sent as - # "None" and "fallback_label_name" is sent. - if table_qualified: - fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - fallback_label_name = c._dedupe_anon_label_idx( - dedupe_hash - ) - dedupe_hash += 1 - else: - fallback_label_name = c._anon_name_label - else: - required_label_name = effective_name = ( - fallback_label_name - ) = expr_label - - if effective_name is not None: - if TYPE_CHECKING: - assert is_column_element(c) - - if effective_name in names: - # when looking to see if names[name] is the same column as - # c, use hash(), so that an annotated version of the column - # is seen as the same as the non-annotated - if hash(names[effective_name]) != hash(c): - # different column under the same name. apply - # disambiguating label - if table_qualified: - required_label_name = fallback_label_name = ( - c._anon_tq_label - ) - else: - required_label_name = fallback_label_name = ( - c._anon_name_label - ) - - if anon_for_dupe_key and required_label_name in names: - # here, c._anon_tq_label is definitely unique to - # that column identity (or annotated version), so - # this should always be true. - # this is also an infrequent codepath because - # you need two levels of duplication to be here - assert hash(names[required_label_name]) == hash(c) - - # the column under the disambiguating label is - # already present. apply the "dedupe" label to - # subsequent occurrences of the column so that the - # original stays non-ambiguous - if table_qualified: - required_label_name = fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - required_label_name = fallback_label_name = ( - c._dedupe_anon_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - repeated = True - else: - names[required_label_name] = c - elif anon_for_dupe_key: - # same column under the same name. apply the "dedupe" - # label so that the original stays non-ambiguous - if table_qualified: - required_label_name = fallback_label_name = ( - c._dedupe_anon_tq_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - else: - required_label_name = fallback_label_name = ( - c._dedupe_anon_label_idx(dedupe_hash) - ) - dedupe_hash += 1 - repeated = True - else: - names[effective_name] = c - - result_append( - _ColumnsPlusNames( - required_label_name, - key_naming_convention(c), - fallback_label_name, - c, - repeated, - ) - ) - - return result - - -class HasCTE(roles.HasCTERole, SelectsRows): - """Mixin that declares a class to include CTE support.""" - - _has_ctes_traverse_internals: _TraverseInternalsType = [ - ("_independent_ctes", InternalTraversal.dp_clauseelement_list), - ("_independent_ctes_opts", InternalTraversal.dp_plain_obj), - ] - - _independent_ctes: Tuple[CTE, ...] = () - _independent_ctes_opts: Tuple[_CTEOpts, ...] = () - - @_generative - def add_cte(self, *ctes: CTE, nest_here: bool = False) -> Self: - r"""Add one or more :class:`_sql.CTE` constructs to this statement. - - This method will associate the given :class:`_sql.CTE` constructs with - the parent statement such that they will each be unconditionally - rendered in the WITH clause of the final statement, even if not - referenced elsewhere within the statement or any sub-selects. - - The optional :paramref:`.HasCTE.add_cte.nest_here` parameter when set - to True will have the effect that each given :class:`_sql.CTE` will - render in a WITH clause rendered directly along with this statement, - rather than being moved to the top of the ultimate rendered statement, - even if this statement is rendered as a subquery within a larger - statement. - - This method has two general uses. One is to embed CTE statements that - serve some purpose without being referenced explicitly, such as the use - case of embedding a DML statement such as an INSERT or UPDATE as a CTE - inline with a primary statement that may draw from its results - indirectly. The other is to provide control over the exact placement - of a particular series of CTE constructs that should remain rendered - directly in terms of a particular statement that may be nested in a - larger statement. - - E.g.:: - - from sqlalchemy import table, column, select - t = table('t', column('c1'), column('c2')) - - ins = t.insert().values({"c1": "x", "c2": "y"}).cte() - - stmt = select(t).add_cte(ins) - - Would render:: - - WITH anon_1 AS - (INSERT INTO t (c1, c2) VALUES (:param_1, :param_2)) - SELECT t.c1, t.c2 - FROM t - - Above, the "anon_1" CTE is not referenced in the SELECT - statement, however still accomplishes the task of running an INSERT - statement. - - Similarly in a DML-related context, using the PostgreSQL - :class:`_postgresql.Insert` construct to generate an "upsert":: - - from sqlalchemy import table, column - from sqlalchemy.dialects.postgresql import insert - - t = table("t", column("c1"), column("c2")) - - delete_statement_cte = ( - t.delete().where(t.c.c1 < 1).cte("deletions") - ) - - insert_stmt = insert(t).values({"c1": 1, "c2": 2}) - update_statement = insert_stmt.on_conflict_do_update( - index_elements=[t.c.c1], - set_={ - "c1": insert_stmt.excluded.c1, - "c2": insert_stmt.excluded.c2, - }, - ).add_cte(delete_statement_cte) - - print(update_statement) - - The above statement renders as:: - - WITH deletions AS - (DELETE FROM t WHERE t.c1 < %(c1_1)s) - INSERT INTO t (c1, c2) VALUES (%(c1)s, %(c2)s) - ON CONFLICT (c1) DO UPDATE SET c1 = excluded.c1, c2 = excluded.c2 - - .. versionadded:: 1.4.21 - - :param \*ctes: zero or more :class:`.CTE` constructs. - - .. versionchanged:: 2.0 Multiple CTE instances are accepted - - :param nest_here: if True, the given CTE or CTEs will be rendered - as though they specified the :paramref:`.HasCTE.cte.nesting` flag - to ``True`` when they were added to this :class:`.HasCTE`. - Assuming the given CTEs are not referenced in an outer-enclosing - statement as well, the CTEs given should render at the level of - this statement when this flag is given. - - .. versionadded:: 2.0 - - .. seealso:: - - :paramref:`.HasCTE.cte.nesting` - - - """ - opt = _CTEOpts( - nest_here, - ) - for cte in ctes: - cte = coercions.expect(roles.IsCTERole, cte) - self._independent_ctes += (cte,) - self._independent_ctes_opts += (opt,) - return self - - def cte( - self, - name: Optional[str] = None, - recursive: bool = False, - nesting: bool = False, - ) -> CTE: - r"""Return a new :class:`_expression.CTE`, - or Common Table Expression instance. - - Common table expressions are a SQL standard whereby SELECT - statements can draw upon secondary statements specified along - with the primary statement, using a clause called "WITH". - Special semantics regarding UNION can also be employed to - allow "recursive" queries, where a SELECT statement can draw - upon the set of rows that have previously been selected. - - CTEs can also be applied to DML constructs UPDATE, INSERT - and DELETE on some databases, both as a source of CTE rows - when combined with RETURNING, as well as a consumer of - CTE rows. - - SQLAlchemy detects :class:`_expression.CTE` objects, which are treated - similarly to :class:`_expression.Alias` objects, as special elements - to be delivered to the FROM clause of the statement as well - as to a WITH clause at the top of the statement. - - For special prefixes such as PostgreSQL "MATERIALIZED" and - "NOT MATERIALIZED", the :meth:`_expression.CTE.prefix_with` - method may be - used to establish these. - - .. versionchanged:: 1.3.13 Added support for prefixes. - In particular - MATERIALIZED and NOT MATERIALIZED. - - :param name: name given to the common table expression. Like - :meth:`_expression.FromClause.alias`, the name can be left as - ``None`` in which case an anonymous symbol will be used at query - compile time. - :param recursive: if ``True``, will render ``WITH RECURSIVE``. - A recursive common table expression is intended to be used in - conjunction with UNION ALL in order to derive rows - from those already selected. - :param nesting: if ``True``, will render the CTE locally to the - statement in which it is referenced. For more complex scenarios, - the :meth:`.HasCTE.add_cte` method using the - :paramref:`.HasCTE.add_cte.nest_here` - parameter may also be used to more carefully - control the exact placement of a particular CTE. - - .. versionadded:: 1.4.24 - - .. seealso:: - - :meth:`.HasCTE.add_cte` - - The following examples include two from PostgreSQL's documentation at - https://www.postgresql.org/docs/current/static/queries-with.html, - as well as additional examples. - - Example 1, non recursive:: - - from sqlalchemy import (Table, Column, String, Integer, - MetaData, select, func) - - metadata = MetaData() - - orders = Table('orders', metadata, - Column('region', String), - Column('amount', Integer), - Column('product', String), - Column('quantity', Integer) - ) - - regional_sales = select( - orders.c.region, - func.sum(orders.c.amount).label('total_sales') - ).group_by(orders.c.region).cte("regional_sales") - - - top_regions = select(regional_sales.c.region).\ - where( - regional_sales.c.total_sales > - select( - func.sum(regional_sales.c.total_sales) / 10 - ) - ).cte("top_regions") - - statement = select( - orders.c.region, - orders.c.product, - func.sum(orders.c.quantity).label("product_units"), - func.sum(orders.c.amount).label("product_sales") - ).where(orders.c.region.in_( - select(top_regions.c.region) - )).group_by(orders.c.region, orders.c.product) - - result = conn.execute(statement).fetchall() - - Example 2, WITH RECURSIVE:: - - from sqlalchemy import (Table, Column, String, Integer, - MetaData, select, func) - - metadata = MetaData() - - parts = Table('parts', metadata, - Column('part', String), - Column('sub_part', String), - Column('quantity', Integer), - ) - - included_parts = select(\ - parts.c.sub_part, parts.c.part, parts.c.quantity\ - ).\ - where(parts.c.part=='our part').\ - cte(recursive=True) - - - incl_alias = included_parts.alias() - parts_alias = parts.alias() - included_parts = included_parts.union_all( - select( - parts_alias.c.sub_part, - parts_alias.c.part, - parts_alias.c.quantity - ).\ - where(parts_alias.c.part==incl_alias.c.sub_part) - ) - - statement = select( - included_parts.c.sub_part, - func.sum(included_parts.c.quantity). - label('total_quantity') - ).\ - group_by(included_parts.c.sub_part) - - result = conn.execute(statement).fetchall() - - Example 3, an upsert using UPDATE and INSERT with CTEs:: - - from datetime import date - from sqlalchemy import (MetaData, Table, Column, Integer, - Date, select, literal, and_, exists) - - metadata = MetaData() - - visitors = Table('visitors', metadata, - Column('product_id', Integer, primary_key=True), - Column('date', Date, primary_key=True), - Column('count', Integer), - ) - - # add 5 visitors for the product_id == 1 - product_id = 1 - day = date.today() - count = 5 - - update_cte = ( - visitors.update() - .where(and_(visitors.c.product_id == product_id, - visitors.c.date == day)) - .values(count=visitors.c.count + count) - .returning(literal(1)) - .cte('update_cte') - ) - - upsert = visitors.insert().from_select( - [visitors.c.product_id, visitors.c.date, visitors.c.count], - select(literal(product_id), literal(day), literal(count)) - .where(~exists(update_cte.select())) - ) - - connection.execute(upsert) - - Example 4, Nesting CTE (SQLAlchemy 1.4.24 and above):: - - value_a = select( - literal("root").label("n") - ).cte("value_a") - - # A nested CTE with the same name as the root one - value_a_nested = select( - literal("nesting").label("n") - ).cte("value_a", nesting=True) - - # Nesting CTEs takes ascendency locally - # over the CTEs at a higher level - value_b = select(value_a_nested.c.n).cte("value_b") - - value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b")) - - The above query will render the second CTE nested inside the first, - shown with inline parameters below as:: - - WITH - value_a AS - (SELECT 'root' AS n), - value_b AS - (WITH value_a AS - (SELECT 'nesting' AS n) - SELECT value_a.n AS n FROM value_a) - SELECT value_a.n AS a, value_b.n AS b - FROM value_a, value_b - - The same CTE can be set up using the :meth:`.HasCTE.add_cte` method - as follows (SQLAlchemy 2.0 and above):: - - value_a = select( - literal("root").label("n") - ).cte("value_a") - - # A nested CTE with the same name as the root one - value_a_nested = select( - literal("nesting").label("n") - ).cte("value_a") - - # Nesting CTEs takes ascendency locally - # over the CTEs at a higher level - value_b = ( - select(value_a_nested.c.n). - add_cte(value_a_nested, nest_here=True). - cte("value_b") - ) - - value_ab = select(value_a.c.n.label("a"), value_b.c.n.label("b")) - - Example 5, Non-Linear CTE (SQLAlchemy 1.4.28 and above):: - - edge = Table( - "edge", - metadata, - Column("id", Integer, primary_key=True), - Column("left", Integer), - Column("right", Integer), - ) - - root_node = select(literal(1).label("node")).cte( - "nodes", recursive=True - ) - - left_edge = select(edge.c.left).join( - root_node, edge.c.right == root_node.c.node - ) - right_edge = select(edge.c.right).join( - root_node, edge.c.left == root_node.c.node - ) - - subgraph_cte = root_node.union(left_edge, right_edge) - - subgraph = select(subgraph_cte) - - The above query will render 2 UNIONs inside the recursive CTE:: - - WITH RECURSIVE nodes(node) AS ( - SELECT 1 AS node - UNION - SELECT edge."left" AS "left" - FROM edge JOIN nodes ON edge."right" = nodes.node - UNION - SELECT edge."right" AS "right" - FROM edge JOIN nodes ON edge."left" = nodes.node - ) - SELECT nodes.node FROM nodes - - .. seealso:: - - :meth:`_orm.Query.cte` - ORM version of - :meth:`_expression.HasCTE.cte`. - - """ - return CTE._construct( - self, name=name, recursive=recursive, nesting=nesting - ) - - -class Subquery(AliasedReturnsRows): - """Represent a subquery of a SELECT. - - A :class:`.Subquery` is created by invoking the - :meth:`_expression.SelectBase.subquery` method, or for convenience the - :meth:`_expression.SelectBase.alias` method, on any - :class:`_expression.SelectBase` subclass - which includes :class:`_expression.Select`, - :class:`_expression.CompoundSelect`, and - :class:`_expression.TextualSelect`. As rendered in a FROM clause, - it represents the - body of the SELECT statement inside of parenthesis, followed by the usual - "AS <somename>" that defines all "alias" objects. - - The :class:`.Subquery` object is very similar to the - :class:`_expression.Alias` - object and can be used in an equivalent way. The difference between - :class:`_expression.Alias` and :class:`.Subquery` is that - :class:`_expression.Alias` always - contains a :class:`_expression.FromClause` object whereas - :class:`.Subquery` - always contains a :class:`_expression.SelectBase` object. - - .. versionadded:: 1.4 The :class:`.Subquery` class was added which now - serves the purpose of providing an aliased version of a SELECT - statement. - - """ - - __visit_name__ = "subquery" - - _is_subquery = True - - inherit_cache = True - - element: SelectBase - - @classmethod - def _factory( - cls, selectable: SelectBase, name: Optional[str] = None - ) -> Subquery: - """Return a :class:`.Subquery` object.""" - - return coercions.expect( - roles.SelectStatementRole, selectable - ).subquery(name=name) - - @util.deprecated( - "1.4", - "The :meth:`.Subquery.as_scalar` method, which was previously " - "``Alias.as_scalar()`` prior to version 1.4, is deprecated and " - "will be removed in a future release; Please use the " - ":meth:`_expression.Select.scalar_subquery` method of the " - ":func:`_expression.select` " - "construct before constructing a subquery object, or with the ORM " - "use the :meth:`_query.Query.scalar_subquery` method.", - ) - def as_scalar(self) -> ScalarSelect[Any]: - return self.element.set_label_style(LABEL_STYLE_NONE).scalar_subquery() - - -class FromGrouping(GroupedElement, FromClause): - """Represent a grouping of a FROM clause""" - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement) - ] - - element: FromClause - - def __init__(self, element: FromClause): - self.element = coercions.expect(roles.FromClauseRole, element) - - def _init_collections(self) -> None: - pass - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.element.columns - - @util.ro_non_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.element.columns - - @property - def primary_key(self) -> Iterable[NamedColumn[Any]]: - return self.element.primary_key - - @property - def foreign_keys(self) -> Iterable[ForeignKey]: - return self.element.foreign_keys - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - return self.element.is_derived_from(fromclause) - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> NamedFromGrouping: - return NamedFromGrouping(self.element.alias(name=name, flat=flat)) - - def _anonymous_fromclause(self, **kw: Any) -> FromGrouping: - return FromGrouping(self.element._anonymous_fromclause(**kw)) - - @util.ro_non_memoized_property - def _hide_froms(self) -> Iterable[FromClause]: - return self.element._hide_froms - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - def __getstate__(self) -> Dict[str, FromClause]: - return {"element": self.element} - - def __setstate__(self, state: Dict[str, FromClause]) -> None: - self.element = state["element"] - - -class NamedFromGrouping(FromGrouping, NamedFromClause): - """represent a grouping of a named FROM clause - - .. versionadded:: 2.0 - - """ - - inherit_cache = True - - -class TableClause(roles.DMLTableRole, Immutable, NamedFromClause): - """Represents a minimal "table" construct. - - This is a lightweight table object that has only a name, a - collection of columns, which are typically produced - by the :func:`_expression.column` function, and a schema:: - - from sqlalchemy import table, column - - user = table("user", - column("id"), - column("name"), - column("description"), - ) - - The :class:`_expression.TableClause` construct serves as the base for - the more commonly used :class:`_schema.Table` object, providing - the usual set of :class:`_expression.FromClause` services including - the ``.c.`` collection and statement generation methods. - - It does **not** provide all the additional schema-level services - of :class:`_schema.Table`, including constraints, references to other - tables, or support for :class:`_schema.MetaData`-level services. - It's useful - on its own as an ad-hoc construct used to generate quick SQL - statements when a more fully fledged :class:`_schema.Table` - is not on hand. - - """ - - __visit_name__ = "table" - - _traverse_internals: _TraverseInternalsType = [ - ( - "columns", - InternalTraversal.dp_fromclause_canonical_column_collection, - ), - ("name", InternalTraversal.dp_string), - ("schema", InternalTraversal.dp_string), - ] - - _is_table = True - - fullname: str - - implicit_returning = False - """:class:`_expression.TableClause` - doesn't support having a primary key or column - -level defaults, so implicit returning doesn't apply.""" - - @util.ro_memoized_property - def _autoincrement_column(self) -> Optional[ColumnClause[Any]]: - """No PK or default support so no autoincrement column.""" - return None - - def __init__(self, name: str, *columns: ColumnClause[Any], **kw: Any): - super().__init__() - self.name = name - self._columns = DedupeColumnCollection() - self.primary_key = ColumnSet() # type: ignore - self.foreign_keys = set() # type: ignore - for c in columns: - self.append_column(c) - - schema = kw.pop("schema", None) - if schema is not None: - self.schema = schema - if self.schema is not None: - self.fullname = "%s.%s" % (self.schema, self.name) - else: - self.fullname = self.name - if kw: - raise exc.ArgumentError("Unsupported argument(s): %s" % list(kw)) - - if TYPE_CHECKING: - - @util.ro_non_memoized_property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]: ... - - @util.ro_non_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]: ... - - def __str__(self) -> str: - if self.schema is not None: - return self.schema + "." + self.name - else: - return self.name - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - pass - - def _init_collections(self) -> None: - pass - - @util.ro_memoized_property - def description(self) -> str: - return self.name - - def append_column(self, c: ColumnClause[Any]) -> None: - existing = c.table - if existing is not None and existing is not self: - raise exc.ArgumentError( - "column object '%s' already assigned to table '%s'" - % (c.key, existing) - ) - - self._columns.add(c) - c.table = self - - @util.preload_module("sqlalchemy.sql.dml") - def insert(self) -> util.preloaded.sql_dml.Insert: - """Generate an :class:`_sql.Insert` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.insert().values(name='foo') - - See :func:`_expression.insert` for argument and usage information. - - """ - - return util.preloaded.sql_dml.Insert(self) - - @util.preload_module("sqlalchemy.sql.dml") - def update(self) -> Update: - """Generate an :func:`_expression.update` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.update().where(table.c.id==7).values(name='foo') - - See :func:`_expression.update` for argument and usage information. - - """ - return util.preloaded.sql_dml.Update( - self, - ) - - @util.preload_module("sqlalchemy.sql.dml") - def delete(self) -> Delete: - """Generate a :func:`_expression.delete` construct against this - :class:`_expression.TableClause`. - - E.g.:: - - table.delete().where(table.c.id==7) - - See :func:`_expression.delete` for argument and usage information. - - """ - return util.preloaded.sql_dml.Delete(self) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [self] - - -ForUpdateParameter = Union["ForUpdateArg", None, bool, Dict[str, Any]] - - -class ForUpdateArg(ClauseElement): - _traverse_internals: _TraverseInternalsType = [ - ("of", InternalTraversal.dp_clauseelement_list), - ("nowait", InternalTraversal.dp_boolean), - ("read", InternalTraversal.dp_boolean), - ("skip_locked", InternalTraversal.dp_boolean), - ] - - of: Optional[Sequence[ClauseElement]] - nowait: bool - read: bool - skip_locked: bool - - @classmethod - def _from_argument( - cls, with_for_update: ForUpdateParameter - ) -> Optional[ForUpdateArg]: - if isinstance(with_for_update, ForUpdateArg): - return with_for_update - elif with_for_update in (None, False): - return None - elif with_for_update is True: - return ForUpdateArg() - else: - return ForUpdateArg(**cast("Dict[str, Any]", with_for_update)) - - def __eq__(self, other: Any) -> bool: - return ( - isinstance(other, ForUpdateArg) - and other.nowait == self.nowait - and other.read == self.read - and other.skip_locked == self.skip_locked - and other.key_share == self.key_share - and other.of is self.of - ) - - def __ne__(self, other: Any) -> bool: - return not self.__eq__(other) - - def __hash__(self) -> int: - return id(self) - - def __init__( - self, - *, - nowait: bool = False, - read: bool = False, - of: Optional[_ForUpdateOfArgument] = None, - skip_locked: bool = False, - key_share: bool = False, - ): - """Represents arguments specified to - :meth:`_expression.Select.for_update`. - - """ - - self.nowait = nowait - self.read = read - self.skip_locked = skip_locked - self.key_share = key_share - if of is not None: - self.of = [ - coercions.expect(roles.ColumnsClauseRole, elem) - for elem in util.to_list(of) - ] - else: - self.of = None - - -class Values(roles.InElementRole, Generative, LateralFromClause): - """Represent a ``VALUES`` construct that can be used as a FROM element - in a statement. - - The :class:`_expression.Values` object is created from the - :func:`_expression.values` function. - - .. versionadded:: 1.4 - - """ - - __visit_name__ = "values" - - _data: Tuple[Sequence[Tuple[Any, ...]], ...] = () - - _unnamed: bool - _traverse_internals: _TraverseInternalsType = [ - ("_column_args", InternalTraversal.dp_clauseelement_list), - ("_data", InternalTraversal.dp_dml_multi_values), - ("name", InternalTraversal.dp_string), - ("literal_binds", InternalTraversal.dp_boolean), - ] - - def __init__( - self, - *columns: ColumnClause[Any], - name: Optional[str] = None, - literal_binds: bool = False, - ): - super().__init__() - self._column_args = columns - - if name is None: - self._unnamed = True - self.name = _anonymous_label.safe_construct(id(self), "anon") - else: - self._unnamed = False - self.name = name - self.literal_binds = literal_binds - self.named_with_column = not self._unnamed - - @property - def _column_types(self) -> List[TypeEngine[Any]]: - return [col.type for col in self._column_args] - - @_generative - def alias(self, name: Optional[str] = None, flat: bool = False) -> Self: - """Return a new :class:`_expression.Values` - construct that is a copy of this - one with the given name. - - This method is a VALUES-specific specialization of the - :meth:`_expression.FromClause.alias` method. - - .. seealso:: - - :ref:`tutorial_using_aliases` - - :func:`_expression.alias` - - """ - non_none_name: str - - if name is None: - non_none_name = _anonymous_label.safe_construct(id(self), "anon") - else: - non_none_name = name - - self.name = non_none_name - self.named_with_column = True - self._unnamed = False - return self - - @_generative - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a new :class:`_expression.Values` with the lateral flag set, - so that - it renders as LATERAL. - - .. seealso:: - - :func:`_expression.lateral` - - """ - non_none_name: str - - if name is None: - non_none_name = self.name - else: - non_none_name = name - - self._is_lateral = True - self.name = non_none_name - self._unnamed = False - return self - - @_generative - def data(self, values: Sequence[Tuple[Any, ...]]) -> Self: - """Return a new :class:`_expression.Values` construct, - adding the given data to the data list. - - E.g.:: - - my_values = my_values.data([(1, 'value 1'), (2, 'value2')]) - - :param values: a sequence (i.e. list) of tuples that map to the - column expressions given in the :class:`_expression.Values` - constructor. - - """ - - self._data += (values,) - return self - - def scalar_values(self) -> ScalarValues: - """Returns a scalar ``VALUES`` construct that can be used as a - COLUMN element in a statement. - - .. versionadded:: 2.0.0b4 - - """ - return ScalarValues(self._column_args, self._data, self.literal_binds) - - def _populate_column_collection(self) -> None: - for c in self._column_args: - if c.table is not None and c.table is not self: - _, c = c._make_proxy(self) - else: - # if the column was used in other contexts, ensure - # no memoizations of other FROM clauses. - # see test_values.py -> test_auto_proxy_select_direct_col - c._reset_memoizations() - self._columns.add(c) - c.table = self - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [self] - - -class ScalarValues(roles.InElementRole, GroupedElement, ColumnElement[Any]): - """Represent a scalar ``VALUES`` construct that can be used as a - COLUMN element in a statement. - - The :class:`_expression.ScalarValues` object is created from the - :meth:`_expression.Values.scalar_values` method. It's also - automatically generated when a :class:`_expression.Values` is used in - an ``IN`` or ``NOT IN`` condition. - - .. versionadded:: 2.0.0b4 - - """ - - __visit_name__ = "scalar_values" - - _traverse_internals: _TraverseInternalsType = [ - ("_column_args", InternalTraversal.dp_clauseelement_list), - ("_data", InternalTraversal.dp_dml_multi_values), - ("literal_binds", InternalTraversal.dp_boolean), - ] - - def __init__( - self, - columns: Sequence[ColumnClause[Any]], - data: Tuple[Sequence[Tuple[Any, ...]], ...], - literal_binds: bool, - ): - super().__init__() - self._column_args = columns - self._data = data - self.literal_binds = literal_binds - - @property - def _column_types(self) -> List[TypeEngine[Any]]: - return [col.type for col in self._column_args] - - def __clause_element__(self) -> ScalarValues: - return self - - -class SelectBase( - roles.SelectStatementRole, - roles.DMLSelectRole, - roles.CompoundElementRole, - roles.InElementRole, - HasCTE, - SupportsCloneAnnotations, - Selectable, -): - """Base class for SELECT statements. - - - This includes :class:`_expression.Select`, - :class:`_expression.CompoundSelect` and - :class:`_expression.TextualSelect`. - - - """ - - _is_select_base = True - is_select = True - - _label_style: SelectLabelStyle = LABEL_STYLE_NONE - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - self._reset_memoizations() - - @util.ro_non_memoized_property - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - .. note:: - - The :attr:`_sql.SelectBase.selected_columns` collection does not - include expressions established in the columns clause using the - :func:`_sql.text` construct; these are silently omitted from the - collection. To use plain textual column expressions inside of a - :class:`_sql.Select` construct, use the :func:`_sql.literal_column` - construct. - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - .. versionadded:: 1.4 - - """ - raise NotImplementedError() - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - raise NotImplementedError() - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - """A sequence of expressions that correspond to what is rendered - in the columns clause, including :class:`_sql.TextClause` - constructs. - - .. versionadded:: 1.4.12 - - .. seealso:: - - :attr:`_sql.SelectBase.exported_columns` - - """ - raise NotImplementedError() - - @property - def exported_columns( - self, - ) -> ReadOnlyColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - that represents the "exported" - columns of this :class:`_expression.Selectable`, not including - :class:`_sql.TextClause` constructs. - - The "exported" columns for a :class:`_expression.SelectBase` - object are synonymous - with the :attr:`_expression.SelectBase.selected_columns` collection. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_expression.Select.exported_columns` - - :attr:`_expression.Selectable.exported_columns` - - :attr:`_expression.FromClause.exported_columns` - - - """ - return self.selected_columns.as_readonly() - - @property - @util.deprecated( - "1.4", - "The :attr:`_expression.SelectBase.c` and " - ":attr:`_expression.SelectBase.columns` attributes " - "are deprecated and will be removed in a future release; these " - "attributes implicitly create a subquery that should be explicit. " - "Please call :meth:`_expression.SelectBase.subquery` " - "first in order to create " - "a subquery, which then contains this attribute. To access the " - "columns that this SELECT object SELECTs " - "from, use the :attr:`_expression.SelectBase.selected_columns` " - "attribute.", - ) - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self._implicit_subquery.columns - - @property - def columns( - self, - ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - return self.c - - def get_label_style(self) -> SelectLabelStyle: - """ - Retrieve the current label style. - - Implemented by subclasses. - - """ - raise NotImplementedError() - - def set_label_style(self, style: SelectLabelStyle) -> Self: - """Return a new selectable with the specified label style. - - Implemented by subclasses. - - """ - - raise NotImplementedError() - - @util.deprecated( - "1.4", - "The :meth:`_expression.SelectBase.select` method is deprecated " - "and will be removed in a future release; this method implicitly " - "creates a subquery that should be explicit. " - "Please call :meth:`_expression.SelectBase.subquery` " - "first in order to create " - "a subquery, which then can be selected.", - ) - def select(self, *arg: Any, **kw: Any) -> Select[Any]: - return self._implicit_subquery.select(*arg, **kw) - - @HasMemoized.memoized_attribute - def _implicit_subquery(self) -> Subquery: - return self.subquery() - - def _scalar_type(self) -> TypeEngine[Any]: - raise NotImplementedError() - - @util.deprecated( - "1.4", - "The :meth:`_expression.SelectBase.as_scalar` " - "method is deprecated and will be " - "removed in a future release. Please refer to " - ":meth:`_expression.SelectBase.scalar_subquery`.", - ) - def as_scalar(self) -> ScalarSelect[Any]: - return self.scalar_subquery() - - def exists(self) -> Exists: - """Return an :class:`_sql.Exists` representation of this selectable, - which can be used as a column expression. - - The returned object is an instance of :class:`_sql.Exists`. - - .. seealso:: - - :func:`_sql.exists` - - :ref:`tutorial_exists` - in the :term:`2.0 style` tutorial. - - .. versionadded:: 1.4 - - """ - return Exists(self) - - def scalar_subquery(self) -> ScalarSelect[Any]: - """Return a 'scalar' representation of this selectable, which can be - used as a column expression. - - The returned object is an instance of :class:`_sql.ScalarSelect`. - - Typically, a select statement which has only one column in its columns - clause is eligible to be used as a scalar expression. The scalar - subquery can then be used in the WHERE clause or columns clause of - an enclosing SELECT. - - Note that the scalar subquery differentiates from the FROM-level - subquery that can be produced using the - :meth:`_expression.SelectBase.subquery` - method. - - .. versionchanged: 1.4 - the ``.as_scalar()`` method was renamed to - :meth:`_expression.SelectBase.scalar_subquery`. - - .. seealso:: - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - """ - if self._label_style is not LABEL_STYLE_NONE: - self = self.set_label_style(LABEL_STYLE_NONE) - - return ScalarSelect(self) - - def label(self, name: Optional[str]) -> Label[Any]: - """Return a 'scalar' representation of this selectable, embedded as a - subquery with a label. - - .. seealso:: - - :meth:`_expression.SelectBase.scalar_subquery`. - - """ - return self.scalar_subquery().label(name) - - def lateral(self, name: Optional[str] = None) -> LateralFromClause: - """Return a LATERAL alias of this :class:`_expression.Selectable`. - - The return value is the :class:`_expression.Lateral` construct also - provided by the top-level :func:`_expression.lateral` function. - - .. seealso:: - - :ref:`tutorial_lateral_correlation` - overview of usage. - - """ - return Lateral._factory(self, name) - - def subquery(self, name: Optional[str] = None) -> Subquery: - """Return a subquery of this :class:`_expression.SelectBase`. - - A subquery is from a SQL perspective a parenthesized, named - construct that can be placed in the FROM clause of another - SELECT statement. - - Given a SELECT statement such as:: - - stmt = select(table.c.id, table.c.name) - - The above statement might look like:: - - SELECT table.id, table.name FROM table - - The subquery form by itself renders the same way, however when - embedded into the FROM clause of another SELECT statement, it becomes - a named sub-element:: - - subq = stmt.subquery() - new_stmt = select(subq) - - The above renders as:: - - SELECT anon_1.id, anon_1.name - FROM (SELECT table.id, table.name FROM table) AS anon_1 - - Historically, :meth:`_expression.SelectBase.subquery` - is equivalent to calling - the :meth:`_expression.FromClause.alias` - method on a FROM object; however, - as a :class:`_expression.SelectBase` - object is not directly FROM object, - the :meth:`_expression.SelectBase.subquery` - method provides clearer semantics. - - .. versionadded:: 1.4 - - """ - - return Subquery._construct( - self._ensure_disambiguated_names(), name=name - ) - - def _ensure_disambiguated_names(self) -> Self: - """Ensure that the names generated by this selectbase will be - disambiguated in some way, if possible. - - """ - - raise NotImplementedError() - - def alias( - self, name: Optional[str] = None, flat: bool = False - ) -> Subquery: - """Return a named subquery against this - :class:`_expression.SelectBase`. - - For a :class:`_expression.SelectBase` (as opposed to a - :class:`_expression.FromClause`), - this returns a :class:`.Subquery` object which behaves mostly the - same as the :class:`_expression.Alias` object that is used with a - :class:`_expression.FromClause`. - - .. versionchanged:: 1.4 The :meth:`_expression.SelectBase.alias` - method is now - a synonym for the :meth:`_expression.SelectBase.subquery` method. - - """ - return self.subquery(name=name) - - -_SB = TypeVar("_SB", bound=SelectBase) - - -class SelectStatementGrouping(GroupedElement, SelectBase, Generic[_SB]): - """Represent a grouping of a :class:`_expression.SelectBase`. - - This differs from :class:`.Subquery` in that we are still - an "inner" SELECT statement, this is strictly for grouping inside of - compound selects. - - """ - - __visit_name__ = "select_statement_grouping" - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement) - ] - - _is_select_container = True - - element: _SB - - def __init__(self, element: _SB) -> None: - self.element = cast( - _SB, coercions.expect(roles.SelectStatementRole, element) - ) - - def _ensure_disambiguated_names(self) -> SelectStatementGrouping[_SB]: - new_element = self.element._ensure_disambiguated_names() - if new_element is not self.element: - return SelectStatementGrouping(new_element) - else: - return self - - def get_label_style(self) -> SelectLabelStyle: - return self.element.get_label_style() - - def set_label_style( - self, label_style: SelectLabelStyle - ) -> SelectStatementGrouping[_SB]: - return SelectStatementGrouping( - self.element.set_label_style(label_style) - ) - - @property - def select_statement(self) -> _SB: - return self.element - - def self_group(self, against: Optional[OperatorType] = None) -> Self: - ... - return self - - if TYPE_CHECKING: - - def _ungroup(self) -> _SB: ... - - # def _generate_columns_plus_names( - # self, anon_for_dupe_key: bool - # ) -> List[Tuple[str, str, str, ColumnElement[Any], bool]]: - # return self.element._generate_columns_plus_names(anon_for_dupe_key) - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - self.element._generate_fromclause_column_proxies( - subquery, proxy_compound_columns=proxy_compound_columns - ) - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.element._all_selected_columns - - @util.ro_non_memoized_property - def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - the embedded SELECT statement returns in its result set, not including - :class:`_sql.TextClause` constructs. - - .. versionadded:: 1.4 - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - """ - return self.element.selected_columns - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return self.element._from_objects - - -class GenerativeSelect(SelectBase, Generative): - """Base class for SELECT statements where additional elements can be - added. - - This serves as the base for :class:`_expression.Select` and - :class:`_expression.CompoundSelect` - where elements such as ORDER BY, GROUP BY can be added and column - rendering can be controlled. Compare to - :class:`_expression.TextualSelect`, which, - while it subclasses :class:`_expression.SelectBase` - and is also a SELECT construct, - represents a fixed textual string which cannot be altered at this level, - only wrapped as a subquery. - - """ - - _order_by_clauses: Tuple[ColumnElement[Any], ...] = () - _group_by_clauses: Tuple[ColumnElement[Any], ...] = () - _limit_clause: Optional[ColumnElement[Any]] = None - _offset_clause: Optional[ColumnElement[Any]] = None - _fetch_clause: Optional[ColumnElement[Any]] = None - _fetch_clause_options: Optional[Dict[str, bool]] = None - _for_update_arg: Optional[ForUpdateArg] = None - - def __init__(self, _label_style: SelectLabelStyle = LABEL_STYLE_DEFAULT): - self._label_style = _label_style - - @_generative - def with_for_update( - self, - *, - nowait: bool = False, - read: bool = False, - of: Optional[_ForUpdateOfArgument] = None, - skip_locked: bool = False, - key_share: bool = False, - ) -> Self: - """Specify a ``FOR UPDATE`` clause for this - :class:`_expression.GenerativeSelect`. - - E.g.:: - - stmt = select(table).with_for_update(nowait=True) - - On a database like PostgreSQL or Oracle, the above would render a - statement like:: - - SELECT table.a, table.b FROM table FOR UPDATE NOWAIT - - on other backends, the ``nowait`` option is ignored and instead - would produce:: - - SELECT table.a, table.b FROM table FOR UPDATE - - When called with no arguments, the statement will render with - the suffix ``FOR UPDATE``. Additional arguments can then be - provided which allow for common database-specific - variants. - - :param nowait: boolean; will render ``FOR UPDATE NOWAIT`` on Oracle - and PostgreSQL dialects. - - :param read: boolean; will render ``LOCK IN SHARE MODE`` on MySQL, - ``FOR SHARE`` on PostgreSQL. On PostgreSQL, when combined with - ``nowait``, will render ``FOR SHARE NOWAIT``. - - :param of: SQL expression or list of SQL expression elements, - (typically :class:`_schema.Column` objects or a compatible expression, - for some backends may also be a table expression) which will render - into a ``FOR UPDATE OF`` clause; supported by PostgreSQL, Oracle, some - MySQL versions and possibly others. May render as a table or as a - column depending on backend. - - :param skip_locked: boolean, will render ``FOR UPDATE SKIP LOCKED`` - on Oracle and PostgreSQL dialects or ``FOR SHARE SKIP LOCKED`` if - ``read=True`` is also specified. - - :param key_share: boolean, will render ``FOR NO KEY UPDATE``, - or if combined with ``read=True`` will render ``FOR KEY SHARE``, - on the PostgreSQL dialect. - - """ - self._for_update_arg = ForUpdateArg( - nowait=nowait, - read=read, - of=of, - skip_locked=skip_locked, - key_share=key_share, - ) - return self - - def get_label_style(self) -> SelectLabelStyle: - """ - Retrieve the current label style. - - .. versionadded:: 1.4 - - """ - return self._label_style - - def set_label_style(self, style: SelectLabelStyle) -> Self: - """Return a new selectable with the specified label style. - - There are three "label styles" available, - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`, - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`, and - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_NONE`. The default style is - :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`. - - In modern SQLAlchemy, there is not generally a need to change the - labeling style, as per-expression labels are more effectively used by - making use of the :meth:`_sql.ColumnElement.label` method. In past - versions, :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` was used to - disambiguate same-named columns from different tables, aliases, or - subqueries; the newer :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` now - applies labels only to names that conflict with an existing name so - that the impact of this labeling is minimal. - - The rationale for disambiguation is mostly so that all column - expressions are available from a given :attr:`_sql.FromClause.c` - collection when a subquery is created. - - .. versionadded:: 1.4 - the - :meth:`_sql.GenerativeSelect.set_label_style` method replaces the - previous combination of ``.apply_labels()``, ``.with_labels()`` and - ``use_labels=True`` methods and/or parameters. - - .. seealso:: - - :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY` - - :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL` - - :data:`_sql.LABEL_STYLE_NONE` - - :data:`_sql.LABEL_STYLE_DEFAULT` - - """ - if self._label_style is not style: - self = self._generate() - self._label_style = style - return self - - @property - def _group_by_clause(self) -> ClauseList: - """ClauseList access to group_by_clauses for legacy dialects""" - return ClauseList._construct_raw( - operators.comma_op, self._group_by_clauses - ) - - @property - def _order_by_clause(self) -> ClauseList: - """ClauseList access to order_by_clauses for legacy dialects""" - return ClauseList._construct_raw( - operators.comma_op, self._order_by_clauses - ) - - def _offset_or_limit_clause( - self, - element: _LimitOffsetType, - name: Optional[str] = None, - type_: Optional[_TypeEngineArgument[int]] = None, - ) -> ColumnElement[Any]: - """Convert the given value to an "offset or limit" clause. - - This handles incoming integers and converts to an expression; if - an expression is already given, it is passed through. - - """ - return coercions.expect( - roles.LimitOffsetRole, element, name=name, type_=type_ - ) - - @overload - def _offset_or_limit_clause_asint( - self, clause: ColumnElement[Any], attrname: str - ) -> NoReturn: ... - - @overload - def _offset_or_limit_clause_asint( - self, clause: Optional[_OffsetLimitParam], attrname: str - ) -> Optional[int]: ... - - def _offset_or_limit_clause_asint( - self, clause: Optional[ColumnElement[Any]], attrname: str - ) -> Union[NoReturn, Optional[int]]: - """Convert the "offset or limit" clause of a select construct to an - integer. - - This is only possible if the value is stored as a simple bound - parameter. Otherwise, a compilation error is raised. - - """ - if clause is None: - return None - try: - value = clause._limit_offset_value - except AttributeError as err: - raise exc.CompileError( - "This SELECT structure does not use a simple " - "integer value for %s" % attrname - ) from err - else: - return util.asint(value) - - @property - def _limit(self) -> Optional[int]: - """Get an integer value for the limit. This should only be used - by code that cannot support a limit as a BindParameter or - other custom clause as it will throw an exception if the limit - isn't currently set to an integer. - - """ - return self._offset_or_limit_clause_asint(self._limit_clause, "limit") - - def _simple_int_clause(self, clause: ClauseElement) -> bool: - """True if the clause is a simple integer, False - if it is not present or is a SQL expression. - """ - return isinstance(clause, _OffsetLimitParam) - - @property - def _offset(self) -> Optional[int]: - """Get an integer value for the offset. This should only be used - by code that cannot support an offset as a BindParameter or - other custom clause as it will throw an exception if the - offset isn't currently set to an integer. - - """ - return self._offset_or_limit_clause_asint( - self._offset_clause, "offset" - ) - - @property - def _has_row_limiting_clause(self) -> bool: - return ( - self._limit_clause is not None - or self._offset_clause is not None - or self._fetch_clause is not None - ) - - @_generative - def limit(self, limit: _LimitOffsetType) -> Self: - """Return a new selectable with the given LIMIT criterion - applied. - - This is a numerical value which usually renders as a ``LIMIT`` - expression in the resulting select. Backends that don't - support ``LIMIT`` will attempt to provide similar - functionality. - - .. note:: - - The :meth:`_sql.GenerativeSelect.limit` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.fetch`. - - :param limit: an integer LIMIT parameter, or a SQL expression - that provides an integer result. Pass ``None`` to reset it. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.fetch` - - :meth:`_sql.GenerativeSelect.offset` - - """ - - self._fetch_clause = self._fetch_clause_options = None - self._limit_clause = self._offset_or_limit_clause(limit) - return self - - @_generative - def fetch( - self, - count: _LimitOffsetType, - with_ties: bool = False, - percent: bool = False, - ) -> Self: - """Return a new selectable with the given FETCH FIRST criterion - applied. - - This is a numeric value which usually renders as - ``FETCH {FIRST | NEXT} [ count ] {ROW | ROWS} {ONLY | WITH TIES}`` - expression in the resulting select. This functionality is - is currently implemented for Oracle, PostgreSQL, MSSQL. - - Use :meth:`_sql.GenerativeSelect.offset` to specify the offset. - - .. note:: - - The :meth:`_sql.GenerativeSelect.fetch` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.limit`. - - .. versionadded:: 1.4 - - :param count: an integer COUNT parameter, or a SQL expression - that provides an integer result. When ``percent=True`` this will - represent the percentage of rows to return, not the absolute value. - Pass ``None`` to reset it. - - :param with_ties: When ``True``, the WITH TIES option is used - to return any additional rows that tie for the last place in the - result set according to the ``ORDER BY`` clause. The - ``ORDER BY`` may be mandatory in this case. Defaults to ``False`` - - :param percent: When ``True``, ``count`` represents the percentage - of the total number of selected rows to return. Defaults to ``False`` - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.offset` - - """ - - self._limit_clause = None - if count is None: - self._fetch_clause = self._fetch_clause_options = None - else: - self._fetch_clause = self._offset_or_limit_clause(count) - self._fetch_clause_options = { - "with_ties": with_ties, - "percent": percent, - } - return self - - @_generative - def offset(self, offset: _LimitOffsetType) -> Self: - """Return a new selectable with the given OFFSET criterion - applied. - - - This is a numeric value which usually renders as an ``OFFSET`` - expression in the resulting select. Backends that don't - support ``OFFSET`` will attempt to provide similar - functionality. - - :param offset: an integer OFFSET parameter, or a SQL expression - that provides an integer result. Pass ``None`` to reset it. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.fetch` - - """ - - self._offset_clause = self._offset_or_limit_clause(offset) - return self - - @_generative - @util.preload_module("sqlalchemy.sql.util") - def slice( - self, - start: int, - stop: int, - ) -> Self: - """Apply LIMIT / OFFSET to this statement based on a slice. - - The start and stop indices behave like the argument to Python's - built-in :func:`range` function. This method provides an - alternative to using ``LIMIT``/``OFFSET`` to get a slice of the - query. - - For example, :: - - stmt = select(User).order_by(User).id.slice(1, 3) - - renders as - - .. sourcecode:: sql - - SELECT users.id AS users_id, - users.name AS users_name - FROM users ORDER BY users.id - LIMIT ? OFFSET ? - (2, 1) - - .. note:: - - The :meth:`_sql.GenerativeSelect.slice` method will replace - any clause applied with :meth:`_sql.GenerativeSelect.fetch`. - - .. versionadded:: 1.4 Added the :meth:`_sql.GenerativeSelect.slice` - method generalized from the ORM. - - .. seealso:: - - :meth:`_sql.GenerativeSelect.limit` - - :meth:`_sql.GenerativeSelect.offset` - - :meth:`_sql.GenerativeSelect.fetch` - - """ - sql_util = util.preloaded.sql_util - self._fetch_clause = self._fetch_clause_options = None - self._limit_clause, self._offset_clause = sql_util._make_slice( - self._limit_clause, self._offset_clause, start, stop - ) - return self - - @_generative - def order_by( - self, - __first: Union[ - Literal[None, _NoArg.NO_ARG], - _ColumnExpressionOrStrLabelArgument[Any], - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionOrStrLabelArgument[Any], - ) -> Self: - r"""Return a new selectable with the given list of ORDER BY - criteria applied. - - e.g.:: - - stmt = select(table).order_by(table.c.id, table.c.name) - - Calling this method multiple times is equivalent to calling it once - with all the clauses concatenated. All existing ORDER BY criteria may - be cancelled by passing ``None`` by itself. New ORDER BY criteria may - then be added by invoking :meth:`_orm.Query.order_by` again, e.g.:: - - # will erase all ORDER BY and ORDER BY new_col alone - stmt = stmt.order_by(None).order_by(new_col) - - :param \*clauses: a series of :class:`_expression.ColumnElement` - constructs - which will be used to generate an ORDER BY clause. - - .. seealso:: - - :ref:`tutorial_order_by` - in the :ref:`unified_tutorial` - - :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial` - - """ - - if not clauses and __first is None: - self._order_by_clauses = () - elif __first is not _NoArg.NO_ARG: - self._order_by_clauses += tuple( - coercions.expect( - roles.OrderByRole, clause, apply_propagate_attrs=self - ) - for clause in (__first,) + clauses - ) - return self - - @_generative - def group_by( - self, - __first: Union[ - Literal[None, _NoArg.NO_ARG], - _ColumnExpressionOrStrLabelArgument[Any], - ] = _NoArg.NO_ARG, - *clauses: _ColumnExpressionOrStrLabelArgument[Any], - ) -> Self: - r"""Return a new selectable with the given list of GROUP BY - criterion applied. - - All existing GROUP BY settings can be suppressed by passing ``None``. - - e.g.:: - - stmt = select(table.c.name, func.max(table.c.stat)).\ - group_by(table.c.name) - - :param \*clauses: a series of :class:`_expression.ColumnElement` - constructs - which will be used to generate an GROUP BY clause. - - .. seealso:: - - :ref:`tutorial_group_by_w_aggregates` - in the - :ref:`unified_tutorial` - - :ref:`tutorial_order_by_label` - in the :ref:`unified_tutorial` - - """ - - if not clauses and __first is None: - self._group_by_clauses = () - elif __first is not _NoArg.NO_ARG: - self._group_by_clauses += tuple( - coercions.expect( - roles.GroupByRole, clause, apply_propagate_attrs=self - ) - for clause in (__first,) + clauses - ) - return self - - -@CompileState.plugin_for("default", "compound_select") -class CompoundSelectState(CompileState): - @util.memoized_property - def _label_resolve_dict( - self, - ) -> Tuple[ - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - ]: - # TODO: this is hacky and slow - hacky_subquery = self.statement.subquery() - hacky_subquery.named_with_column = False - d = {c.key: c for c in hacky_subquery.c} - return d, d, d - - -class _CompoundSelectKeyword(Enum): - UNION = "UNION" - UNION_ALL = "UNION ALL" - EXCEPT = "EXCEPT" - EXCEPT_ALL = "EXCEPT ALL" - INTERSECT = "INTERSECT" - INTERSECT_ALL = "INTERSECT ALL" - - -class CompoundSelect(HasCompileState, GenerativeSelect, ExecutableReturnsRows): - """Forms the basis of ``UNION``, ``UNION ALL``, and other - SELECT-based set operations. - - - .. seealso:: - - :func:`_expression.union` - - :func:`_expression.union_all` - - :func:`_expression.intersect` - - :func:`_expression.intersect_all` - - :func:`_expression.except` - - :func:`_expression.except_all` - - """ - - __visit_name__ = "compound_select" - - _traverse_internals: _TraverseInternalsType = [ - ("selects", InternalTraversal.dp_clauseelement_list), - ("_limit_clause", InternalTraversal.dp_clauseelement), - ("_offset_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause_options", InternalTraversal.dp_plain_dict), - ("_order_by_clauses", InternalTraversal.dp_clauseelement_list), - ("_group_by_clauses", InternalTraversal.dp_clauseelement_list), - ("_for_update_arg", InternalTraversal.dp_clauseelement), - ("keyword", InternalTraversal.dp_string), - ] + SupportsCloneAnnotations._clone_annotations_traverse_internals - - selects: List[SelectBase] - - _is_from_container = True - _auto_correlate = False - - def __init__( - self, - keyword: _CompoundSelectKeyword, - *selects: _SelectStatementForCompoundArgument, - ): - self.keyword = keyword - self.selects = [ - coercions.expect( - roles.CompoundElementRole, s, apply_propagate_attrs=self - ).self_group(against=self) - for s in selects - ] - - GenerativeSelect.__init__(self) - - @classmethod - def _create_union( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.UNION, *selects) - - @classmethod - def _create_union_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.UNION_ALL, *selects) - - @classmethod - def _create_except( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.EXCEPT, *selects) - - @classmethod - def _create_except_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.EXCEPT_ALL, *selects) - - @classmethod - def _create_intersect( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.INTERSECT, *selects) - - @classmethod - def _create_intersect_all( - cls, *selects: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - return CompoundSelect(_CompoundSelectKeyword.INTERSECT_ALL, *selects) - - def _scalar_type(self) -> TypeEngine[Any]: - return self.selects[0]._scalar_type() - - def self_group( - self, against: Optional[OperatorType] = None - ) -> GroupedElement: - return SelectStatementGrouping(self) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - for s in self.selects: - if s.is_derived_from(fromclause): - return True - return False - - def set_label_style(self, style: SelectLabelStyle) -> CompoundSelect: - if self._label_style is not style: - self = self._generate() - select_0 = self.selects[0].set_label_style(style) - self.selects = [select_0] + self.selects[1:] - - return self - - def _ensure_disambiguated_names(self) -> CompoundSelect: - new_select = self.selects[0]._ensure_disambiguated_names() - if new_select is not self.selects[0]: - self = self._generate() - self.selects = [new_select] + self.selects[1:] - - return self - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - # this is a slightly hacky thing - the union exports a - # column that resembles just that of the *first* selectable. - # to get at a "composite" column, particularly foreign keys, - # you have to dig through the proxies collection which we - # generate below. - select_0 = self.selects[0] - - if self._label_style is not LABEL_STYLE_DEFAULT: - select_0 = select_0.set_label_style(self._label_style) - - # hand-construct the "_proxies" collection to include all - # derived columns place a 'weight' annotation corresponding - # to how low in the list of select()s the column occurs, so - # that the corresponding_column() operation can resolve - # conflicts - extra_col_iterator = zip( - *[ - [ - c._annotate(dd) - for c in stmt._all_selected_columns - if is_column_element(c) - ] - for dd, stmt in [ - ({"weight": i + 1}, stmt) - for i, stmt in enumerate(self.selects) - ] - ] - ) - - # the incoming proxy_compound_columns can be present also if this is - # a compound embedded in a compound. it's probably more appropriate - # that we generate new weights local to this nested compound, though - # i haven't tried to think what it means for compound nested in - # compound - select_0._generate_fromclause_column_proxies( - subquery, proxy_compound_columns=extra_col_iterator - ) - - def _refresh_for_new_column(self, column: ColumnElement[Any]) -> None: - super()._refresh_for_new_column(column) - for select in self.selects: - select._refresh_for_new_column(column) - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.selects[0]._all_selected_columns - - @util.ro_non_memoized_property - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - For a :class:`_expression.CompoundSelect`, the - :attr:`_expression.CompoundSelect.selected_columns` - attribute returns the selected - columns of the first SELECT statement contained within the series of - statements within the set operation. - - .. seealso:: - - :attr:`_sql.Select.selected_columns` - - .. versionadded:: 1.4 - - """ - return self.selects[0].selected_columns - - -# backwards compat -for elem in _CompoundSelectKeyword: - setattr(CompoundSelect, elem.name, elem) - - -@CompileState.plugin_for("default", "select") -class SelectState(util.MemoizedSlots, CompileState): - __slots__ = ( - "from_clauses", - "froms", - "columns_plus_names", - "_label_resolve_dict", - ) - - if TYPE_CHECKING: - default_select_compile_options: CacheableOptions - else: - - class default_select_compile_options(CacheableOptions): - _cache_key_traversal = [] - - if TYPE_CHECKING: - - @classmethod - def get_plugin_class( - cls, statement: Executable - ) -> Type[SelectState]: ... - - def __init__( - self, - statement: Select[Any], - compiler: Optional[SQLCompiler], - **kw: Any, - ): - self.statement = statement - self.from_clauses = statement._from_obj - - for memoized_entities in statement._memoized_select_entities: - self._setup_joins( - memoized_entities._setup_joins, memoized_entities._raw_columns - ) - - if statement._setup_joins: - self._setup_joins(statement._setup_joins, statement._raw_columns) - - self.froms = self._get_froms(statement) - - self.columns_plus_names = statement._generate_columns_plus_names(True) - - @classmethod - def _plugin_not_implemented(cls) -> NoReturn: - raise NotImplementedError( - "The default SELECT construct without plugins does not " - "implement this method." - ) - - @classmethod - def get_column_descriptions( - cls, statement: Select[Any] - ) -> List[Dict[str, Any]]: - return [ - { - "name": name, - "type": element.type, - "expr": element, - } - for _, name, _, element, _ in ( - statement._generate_columns_plus_names(False) - ) - ] - - @classmethod - def from_statement( - cls, statement: Select[Any], from_statement: roles.ReturnsRowsRole - ) -> ExecutableReturnsRows: - cls._plugin_not_implemented() - - @classmethod - def get_columns_clause_froms( - cls, statement: Select[Any] - ) -> List[FromClause]: - return cls._normalize_froms( - itertools.chain.from_iterable( - element._from_objects for element in statement._raw_columns - ) - ) - - @classmethod - def _column_naming_convention( - cls, label_style: SelectLabelStyle - ) -> _LabelConventionCallable: - table_qualified = label_style is LABEL_STYLE_TABLENAME_PLUS_COL - dedupe = label_style is not LABEL_STYLE_NONE - - pa = prefix_anon_map() - names = set() - - def go( - c: Union[ColumnElement[Any], TextClause], - col_name: Optional[str] = None, - ) -> Optional[str]: - if is_text_clause(c): - return None - elif TYPE_CHECKING: - assert is_column_element(c) - - if not dedupe: - name = c._proxy_key - if name is None: - name = "_no_label" - return name - - name = c._tq_key_label if table_qualified else c._proxy_key - - if name is None: - name = "_no_label" - if name in names: - return c._anon_label(name) % pa - else: - names.add(name) - return name - - elif name in names: - return ( - c._anon_tq_key_label % pa - if table_qualified - else c._anon_key_label % pa - ) - else: - names.add(name) - return name - - return go - - def _get_froms(self, statement: Select[Any]) -> List[FromClause]: - ambiguous_table_name_map: _AmbiguousTableNameMap - self._ambiguous_table_name_map = ambiguous_table_name_map = {} - - return self._normalize_froms( - itertools.chain( - self.from_clauses, - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._raw_columns - ] - ), - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._where_criteria - ] - ), - ), - check_statement=statement, - ambiguous_table_name_map=ambiguous_table_name_map, - ) - - @classmethod - def _normalize_froms( - cls, - iterable_of_froms: Iterable[FromClause], - check_statement: Optional[Select[Any]] = None, - ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None, - ) -> List[FromClause]: - """given an iterable of things to select FROM, reduce them to what - would actually render in the FROM clause of a SELECT. - - This does the job of checking for JOINs, tables, etc. that are in fact - overlapping due to cloning, adaption, present in overlapping joins, - etc. - - """ - seen: Set[FromClause] = set() - froms: List[FromClause] = [] - - for item in iterable_of_froms: - if is_subquery(item) and item.element is check_statement: - raise exc.InvalidRequestError( - "select() construct refers to itself as a FROM" - ) - - if not seen.intersection(item._cloned_set): - froms.append(item) - seen.update(item._cloned_set) - - if froms: - toremove = set( - itertools.chain.from_iterable( - [_expand_cloned(f._hide_froms) for f in froms] - ) - ) - if toremove: - # filter out to FROM clauses not in the list, - # using a list to maintain ordering - froms = [f for f in froms if f not in toremove] - - if ambiguous_table_name_map is not None: - ambiguous_table_name_map.update( - ( - fr.name, - _anonymous_label.safe_construct( - hash(fr.name), fr.name - ), - ) - for item in froms - for fr in item._from_objects - if is_table(fr) - and fr.schema - and fr.name not in ambiguous_table_name_map - ) - - return froms - - def _get_display_froms( - self, - explicit_correlate_froms: Optional[Sequence[FromClause]] = None, - implicit_correlate_froms: Optional[Sequence[FromClause]] = None, - ) -> List[FromClause]: - """Return the full list of 'from' clauses to be displayed. - - Takes into account a set of existing froms which may be - rendered in the FROM clause of enclosing selects; this Select - may want to leave those absent if it is automatically - correlating. - - """ - - froms = self.froms - - if self.statement._correlate: - to_correlate = self.statement._correlate - if to_correlate: - froms = [ - f - for f in froms - if f - not in _cloned_intersection( - _cloned_intersection( - froms, explicit_correlate_froms or () - ), - to_correlate, - ) - ] - - if self.statement._correlate_except is not None: - froms = [ - f - for f in froms - if f - not in _cloned_difference( - _cloned_intersection( - froms, explicit_correlate_froms or () - ), - self.statement._correlate_except, - ) - ] - - if ( - self.statement._auto_correlate - and implicit_correlate_froms - and len(froms) > 1 - ): - froms = [ - f - for f in froms - if f - not in _cloned_intersection(froms, implicit_correlate_froms) - ] - - if not len(froms): - raise exc.InvalidRequestError( - "Select statement '%r" - "' returned no FROM clauses " - "due to auto-correlation; " - "specify correlate(<tables>) " - "to control correlation " - "manually." % self.statement - ) - - return froms - - def _memoized_attr__label_resolve_dict( - self, - ) -> Tuple[ - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - Dict[str, ColumnElement[Any]], - ]: - with_cols: Dict[str, ColumnElement[Any]] = { - c._tq_label or c.key: c - for c in self.statement._all_selected_columns - if c._allow_label_resolve - } - only_froms: Dict[str, ColumnElement[Any]] = { - c.key: c # type: ignore - for c in _select_iterables(self.froms) - if c._allow_label_resolve - } - only_cols: Dict[str, ColumnElement[Any]] = with_cols.copy() - for key, value in only_froms.items(): - with_cols.setdefault(key, value) - - return with_cols, only_froms, only_cols - - @classmethod - def determine_last_joined_entity( - cls, stmt: Select[Any] - ) -> Optional[_JoinTargetElement]: - if stmt._setup_joins: - return stmt._setup_joins[-1][0] - else: - return None - - @classmethod - def all_selected_columns(cls, statement: Select[Any]) -> _SelectIterable: - return [c for c in _select_iterables(statement._raw_columns)] - - def _setup_joins( - self, - args: Tuple[_SetupJoinsElement, ...], - raw_columns: List[_ColumnsClauseElement], - ) -> None: - for right, onclause, left, flags in args: - if TYPE_CHECKING: - if onclause is not None: - assert isinstance(onclause, ColumnElement) - - isouter = flags["isouter"] - full = flags["full"] - - if left is None: - ( - left, - replace_from_obj_index, - ) = self._join_determine_implicit_left_side( - raw_columns, left, right, onclause - ) - else: - (replace_from_obj_index) = self._join_place_explicit_left_side( - left - ) - - # these assertions can be made here, as if the right/onclause - # contained ORM elements, the select() statement would have been - # upgraded to an ORM select, and this method would not be called; - # orm.context.ORMSelectCompileState._join() would be - # used instead. - if TYPE_CHECKING: - assert isinstance(right, FromClause) - if onclause is not None: - assert isinstance(onclause, ColumnElement) - - if replace_from_obj_index is not None: - # splice into an existing element in the - # self._from_obj list - left_clause = self.from_clauses[replace_from_obj_index] - - self.from_clauses = ( - self.from_clauses[:replace_from_obj_index] - + ( - Join( - left_clause, - right, - onclause, - isouter=isouter, - full=full, - ), - ) - + self.from_clauses[replace_from_obj_index + 1 :] - ) - else: - assert left is not None - self.from_clauses = self.from_clauses + ( - Join(left, right, onclause, isouter=isouter, full=full), - ) - - @util.preload_module("sqlalchemy.sql.util") - def _join_determine_implicit_left_side( - self, - raw_columns: List[_ColumnsClauseElement], - left: Optional[FromClause], - right: _JoinTargetElement, - onclause: Optional[ColumnElement[Any]], - ) -> Tuple[Optional[FromClause], Optional[int]]: - """When join conditions don't express the left side explicitly, - determine if an existing FROM or entity in this query - can serve as the left hand side. - - """ - - sql_util = util.preloaded.sql_util - - replace_from_obj_index: Optional[int] = None - - from_clauses = self.from_clauses - - if from_clauses: - indexes: List[int] = sql_util.find_left_clause_to_join_from( - from_clauses, right, onclause - ) - - if len(indexes) == 1: - replace_from_obj_index = indexes[0] - left = from_clauses[replace_from_obj_index] - else: - potential = {} - statement = self.statement - - for from_clause in itertools.chain( - itertools.chain.from_iterable( - [element._from_objects for element in raw_columns] - ), - itertools.chain.from_iterable( - [ - element._from_objects - for element in statement._where_criteria - ] - ), - ): - potential[from_clause] = () - - all_clauses = list(potential.keys()) - indexes = sql_util.find_left_clause_to_join_from( - all_clauses, right, onclause - ) - - if len(indexes) == 1: - left = all_clauses[indexes[0]] - - if len(indexes) > 1: - raise exc.InvalidRequestError( - "Can't determine which FROM clause to join " - "from, there are multiple FROMS which can " - "join to this entity. Please use the .select_from() " - "method to establish an explicit left side, as well as " - "providing an explicit ON clause if not present already to " - "help resolve the ambiguity." - ) - elif not indexes: - raise exc.InvalidRequestError( - "Don't know how to join to %r. " - "Please use the .select_from() " - "method to establish an explicit left side, as well as " - "providing an explicit ON clause if not present already to " - "help resolve the ambiguity." % (right,) - ) - return left, replace_from_obj_index - - @util.preload_module("sqlalchemy.sql.util") - def _join_place_explicit_left_side( - self, left: FromClause - ) -> Optional[int]: - replace_from_obj_index: Optional[int] = None - - sql_util = util.preloaded.sql_util - - from_clauses = list(self.statement._iterate_from_elements()) - - if from_clauses: - indexes: List[int] = sql_util.find_left_clause_that_matches_given( - self.from_clauses, left - ) - else: - indexes = [] - - if len(indexes) > 1: - raise exc.InvalidRequestError( - "Can't identify which entity in which to assign the " - "left side of this join. Please use a more specific " - "ON clause." - ) - - # have an index, means the left side is already present in - # an existing FROM in the self._from_obj tuple - if indexes: - replace_from_obj_index = indexes[0] - - # no index, means we need to add a new element to the - # self._from_obj tuple - - return replace_from_obj_index - - -class _SelectFromElements: - __slots__ = () - - _raw_columns: List[_ColumnsClauseElement] - _where_criteria: Tuple[ColumnElement[Any], ...] - _from_obj: Tuple[FromClause, ...] - - def _iterate_from_elements(self) -> Iterator[FromClause]: - # note this does not include elements - # in _setup_joins - - seen = set() - for element in self._raw_columns: - for fr in element._from_objects: - if fr in seen: - continue - seen.add(fr) - yield fr - for element in self._where_criteria: - for fr in element._from_objects: - if fr in seen: - continue - seen.add(fr) - yield fr - for element in self._from_obj: - if element in seen: - continue - seen.add(element) - yield element - - -class _MemoizedSelectEntities( - cache_key.HasCacheKey, traversals.HasCopyInternals, visitors.Traversible -): - """represents partial state from a Select object, for the case - where Select.columns() has redefined the set of columns/entities the - statement will be SELECTing from. This object represents - the entities from the SELECT before that transformation was applied, - so that transformations that were made in terms of the SELECT at that - time, such as join() as well as options(), can access the correct context. - - In previous SQLAlchemy versions, this wasn't needed because these - constructs calculated everything up front, like when you called join() - or options(), it did everything to figure out how that would translate - into specific SQL constructs that would be ready to send directly to the - SQL compiler when needed. But as of - 1.4, all of that stuff is done in the compilation phase, during the - "compile state" portion of the process, so that the work can all be - cached. So it needs to be able to resolve joins/options2 based on what - the list of entities was when those methods were called. - - - """ - - __visit_name__ = "memoized_select_entities" - - _traverse_internals: _TraverseInternalsType = [ - ("_raw_columns", InternalTraversal.dp_clauseelement_list), - ("_setup_joins", InternalTraversal.dp_setup_join_tuple), - ("_with_options", InternalTraversal.dp_executable_options), - ] - - _is_clone_of: Optional[ClauseElement] - _raw_columns: List[_ColumnsClauseElement] - _setup_joins: Tuple[_SetupJoinsElement, ...] - _with_options: Tuple[ExecutableOption, ...] - - _annotations = util.EMPTY_DICT - - def _clone(self, **kw: Any) -> Self: - c = self.__class__.__new__(self.__class__) - c.__dict__ = {k: v for k, v in self.__dict__.items()} - - c._is_clone_of = self.__dict__.get("_is_clone_of", self) - return c - - @classmethod - def _generate_for_statement(cls, select_stmt: Select[Any]) -> None: - if select_stmt._setup_joins or select_stmt._with_options: - self = _MemoizedSelectEntities() - self._raw_columns = select_stmt._raw_columns - self._setup_joins = select_stmt._setup_joins - self._with_options = select_stmt._with_options - - select_stmt._memoized_select_entities += (self,) - select_stmt._raw_columns = [] - select_stmt._setup_joins = select_stmt._with_options = () - - -class Select( - HasPrefixes, - HasSuffixes, - HasHints, - HasCompileState, - _SelectFromElements, - GenerativeSelect, - TypedReturnsRows[_TP], -): - """Represents a ``SELECT`` statement. - - The :class:`_sql.Select` object is normally constructed using the - :func:`_sql.select` function. See that function for details. - - .. seealso:: - - :func:`_sql.select` - - :ref:`tutorial_selecting_data` - in the 2.0 tutorial - - """ - - __visit_name__ = "select" - - _setup_joins: Tuple[_SetupJoinsElement, ...] = () - _memoized_select_entities: Tuple[TODO_Any, ...] = () - - _raw_columns: List[_ColumnsClauseElement] - - _distinct: bool = False - _distinct_on: Tuple[ColumnElement[Any], ...] = () - _correlate: Tuple[FromClause, ...] = () - _correlate_except: Optional[Tuple[FromClause, ...]] = None - _where_criteria: Tuple[ColumnElement[Any], ...] = () - _having_criteria: Tuple[ColumnElement[Any], ...] = () - _from_obj: Tuple[FromClause, ...] = () - _auto_correlate = True - _is_select_statement = True - _compile_options: CacheableOptions = ( - SelectState.default_select_compile_options - ) - - _traverse_internals: _TraverseInternalsType = ( - [ - ("_raw_columns", InternalTraversal.dp_clauseelement_list), - ( - "_memoized_select_entities", - InternalTraversal.dp_memoized_select_entities, - ), - ("_from_obj", InternalTraversal.dp_clauseelement_list), - ("_where_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_having_criteria", InternalTraversal.dp_clauseelement_tuple), - ("_order_by_clauses", InternalTraversal.dp_clauseelement_tuple), - ("_group_by_clauses", InternalTraversal.dp_clauseelement_tuple), - ("_setup_joins", InternalTraversal.dp_setup_join_tuple), - ("_correlate", InternalTraversal.dp_clauseelement_tuple), - ("_correlate_except", InternalTraversal.dp_clauseelement_tuple), - ("_limit_clause", InternalTraversal.dp_clauseelement), - ("_offset_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause", InternalTraversal.dp_clauseelement), - ("_fetch_clause_options", InternalTraversal.dp_plain_dict), - ("_for_update_arg", InternalTraversal.dp_clauseelement), - ("_distinct", InternalTraversal.dp_boolean), - ("_distinct_on", InternalTraversal.dp_clauseelement_tuple), - ("_label_style", InternalTraversal.dp_plain_obj), - ] - + HasCTE._has_ctes_traverse_internals - + HasPrefixes._has_prefixes_traverse_internals - + HasSuffixes._has_suffixes_traverse_internals - + HasHints._has_hints_traverse_internals - + SupportsCloneAnnotations._clone_annotations_traverse_internals - + Executable._executable_traverse_internals - ) - - _cache_key_traversal: _CacheKeyTraversalType = _traverse_internals + [ - ("_compile_options", InternalTraversal.dp_has_cache_key) - ] - - _compile_state_factory: Type[SelectState] - - @classmethod - def _create_raw_select(cls, **kw: Any) -> Select[Any]: - """Create a :class:`.Select` using raw ``__new__`` with no coercions. - - Used internally to build up :class:`.Select` constructs with - pre-established state. - - """ - - stmt = Select.__new__(Select) - stmt.__dict__.update(kw) - return stmt - - def __init__(self, *entities: _ColumnsClauseArgument[Any]): - r"""Construct a new :class:`_expression.Select`. - - The public constructor for :class:`_expression.Select` is the - :func:`_sql.select` function. - - """ - self._raw_columns = [ - coercions.expect( - roles.ColumnsClauseRole, ent, apply_propagate_attrs=self - ) - for ent in entities - ] - - GenerativeSelect.__init__(self) - - def _scalar_type(self) -> TypeEngine[Any]: - if not self._raw_columns: - return NULLTYPE - elem = self._raw_columns[0] - cols = list(elem._select_iterable) - return cols[0].type - - def filter(self, *criteria: _ColumnExpressionArgument[bool]) -> Self: - """A synonym for the :meth:`_sql.Select.where` method.""" - - return self.where(*criteria) - - def _filter_by_zero( - self, - ) -> Union[ - FromClause, _JoinTargetProtocol, ColumnElement[Any], TextClause - ]: - if self._setup_joins: - meth = SelectState.get_plugin_class( - self - ).determine_last_joined_entity - _last_joined_entity = meth(self) - if _last_joined_entity is not None: - return _last_joined_entity - - if self._from_obj: - return self._from_obj[0] - - return self._raw_columns[0] - - if TYPE_CHECKING: - - @overload - def scalar_subquery( - self: Select[Tuple[_MAYBE_ENTITY]], - ) -> ScalarSelect[Any]: ... - - @overload - def scalar_subquery( - self: Select[Tuple[_NOT_ENTITY]], - ) -> ScalarSelect[_NOT_ENTITY]: ... - - @overload - def scalar_subquery(self) -> ScalarSelect[Any]: ... - - def scalar_subquery(self) -> ScalarSelect[Any]: ... - - def filter_by(self, **kwargs: Any) -> Self: - r"""apply the given filtering criterion as a WHERE clause - to this select. - - """ - from_entity = self._filter_by_zero() - - clauses = [ - _entity_namespace_key(from_entity, key) == value - for key, value in kwargs.items() - ] - return self.filter(*clauses) - - @property - def column_descriptions(self) -> Any: - """Return a :term:`plugin-enabled` 'column descriptions' structure - referring to the columns which are SELECTed by this statement. - - This attribute is generally useful when using the ORM, as an - extended structure which includes information about mapped - entities is returned. The section :ref:`queryguide_inspection` - contains more background. - - For a Core-only statement, the structure returned by this accessor - is derived from the same objects that are returned by the - :attr:`.Select.selected_columns` accessor, formatted as a list of - dictionaries which contain the keys ``name``, ``type`` and ``expr``, - which indicate the column expressions to be selected:: - - >>> stmt = select(user_table) - >>> stmt.column_descriptions - [ - { - 'name': 'id', - 'type': Integer(), - 'expr': Column('id', Integer(), ...)}, - { - 'name': 'name', - 'type': String(length=30), - 'expr': Column('name', String(length=30), ...)} - ] - - .. versionchanged:: 1.4.33 The :attr:`.Select.column_descriptions` - attribute returns a structure for a Core-only set of entities, - not just ORM-only entities. - - .. seealso:: - - :attr:`.UpdateBase.entity_description` - entity information for - an :func:`.insert`, :func:`.update`, or :func:`.delete` - - :ref:`queryguide_inspection` - ORM background - - """ - meth = SelectState.get_plugin_class(self).get_column_descriptions - return meth(self) - - def from_statement( - self, statement: roles.ReturnsRowsRole - ) -> ExecutableReturnsRows: - """Apply the columns which this :class:`.Select` would select - onto another statement. - - This operation is :term:`plugin-specific` and will raise a not - supported exception if this :class:`_sql.Select` does not select from - plugin-enabled entities. - - - The statement is typically either a :func:`_expression.text` or - :func:`_expression.select` construct, and should return the set of - columns appropriate to the entities represented by this - :class:`.Select`. - - .. seealso:: - - :ref:`orm_queryguide_selecting_text` - usage examples in the - ORM Querying Guide - - """ - meth = SelectState.get_plugin_class(self).from_statement - return meth(self, statement) - - @_generative - def join( - self, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - isouter: bool = False, - full: bool = False, - ) -> Self: - r"""Create a SQL JOIN against this :class:`_expression.Select` - object's criterion - and apply generatively, returning the newly resulting - :class:`_expression.Select`. - - E.g.:: - - stmt = select(user_table).join(address_table, user_table.c.id == address_table.c.user_id) - - The above statement generates SQL similar to:: - - SELECT user.id, user.name FROM user JOIN address ON user.id = address.user_id - - .. versionchanged:: 1.4 :meth:`_expression.Select.join` now creates - a :class:`_sql.Join` object between a :class:`_sql.FromClause` - source that is within the FROM clause of the existing SELECT, - and a given target :class:`_sql.FromClause`, and then adds - this :class:`_sql.Join` to the FROM clause of the newly generated - SELECT statement. This is completely reworked from the behavior - in 1.3, which would instead create a subquery of the entire - :class:`_expression.Select` and then join that subquery to the - target. - - This is a **backwards incompatible change** as the previous behavior - was mostly useless, producing an unnamed subquery rejected by - most databases in any case. The new behavior is modeled after - that of the very successful :meth:`_orm.Query.join` method in the - ORM, in order to support the functionality of :class:`_orm.Query` - being available by using a :class:`_sql.Select` object with an - :class:`_orm.Session`. - - See the notes for this change at :ref:`change_select_join`. - - - :param target: target table to join towards - - :param onclause: ON clause of the join. If omitted, an ON clause - is generated automatically based on the :class:`_schema.ForeignKey` - linkages between the two tables, if one can be unambiguously - determined, otherwise an error is raised. - - :param isouter: if True, generate LEFT OUTER join. Same as - :meth:`_expression.Select.outerjoin`. - - :param full: if True, generate FULL OUTER join. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join_from` - - :meth:`_expression.Select.outerjoin` - - """ # noqa: E501 - join_target = coercions.expect( - roles.JoinTargetRole, target, apply_propagate_attrs=self - ) - if onclause is not None: - onclause_element = coercions.expect(roles.OnClauseRole, onclause) - else: - onclause_element = None - - self._setup_joins += ( - ( - join_target, - onclause_element, - None, - {"isouter": isouter, "full": full}, - ), - ) - return self - - def outerjoin_from( - self, - from_: _FromClauseArgument, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - full: bool = False, - ) -> Self: - r"""Create a SQL LEFT OUTER JOIN against this - :class:`_expression.Select` object's criterion and apply generatively, - returning the newly resulting :class:`_expression.Select`. - - Usage is the same as that of :meth:`_selectable.Select.join_from`. - - """ - return self.join_from( - from_, target, onclause=onclause, isouter=True, full=full - ) - - @_generative - def join_from( - self, - from_: _FromClauseArgument, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - isouter: bool = False, - full: bool = False, - ) -> Self: - r"""Create a SQL JOIN against this :class:`_expression.Select` - object's criterion - and apply generatively, returning the newly resulting - :class:`_expression.Select`. - - E.g.:: - - stmt = select(user_table, address_table).join_from( - user_table, address_table, user_table.c.id == address_table.c.user_id - ) - - The above statement generates SQL similar to:: - - SELECT user.id, user.name, address.id, address.email, address.user_id - FROM user JOIN address ON user.id = address.user_id - - .. versionadded:: 1.4 - - :param from\_: the left side of the join, will be rendered in the - FROM clause and is roughly equivalent to using the - :meth:`.Select.select_from` method. - - :param target: target table to join towards - - :param onclause: ON clause of the join. - - :param isouter: if True, generate LEFT OUTER join. Same as - :meth:`_expression.Select.outerjoin`. - - :param full: if True, generate FULL OUTER join. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join` - - """ # noqa: E501 - - # note the order of parsing from vs. target is important here, as we - # are also deriving the source of the plugin (i.e. the subject mapper - # in an ORM query) which should favor the "from_" over the "target" - - from_ = coercions.expect( - roles.FromClauseRole, from_, apply_propagate_attrs=self - ) - join_target = coercions.expect( - roles.JoinTargetRole, target, apply_propagate_attrs=self - ) - if onclause is not None: - onclause_element = coercions.expect(roles.OnClauseRole, onclause) - else: - onclause_element = None - - self._setup_joins += ( - ( - join_target, - onclause_element, - from_, - {"isouter": isouter, "full": full}, - ), - ) - return self - - def outerjoin( - self, - target: _JoinTargetArgument, - onclause: Optional[_OnClauseArgument] = None, - *, - full: bool = False, - ) -> Self: - """Create a left outer join. - - Parameters are the same as that of :meth:`_expression.Select.join`. - - .. versionchanged:: 1.4 :meth:`_expression.Select.outerjoin` now - creates a :class:`_sql.Join` object between a - :class:`_sql.FromClause` source that is within the FROM clause of - the existing SELECT, and a given target :class:`_sql.FromClause`, - and then adds this :class:`_sql.Join` to the FROM clause of the - newly generated SELECT statement. This is completely reworked - from the behavior in 1.3, which would instead create a subquery of - the entire - :class:`_expression.Select` and then join that subquery to the - target. - - This is a **backwards incompatible change** as the previous behavior - was mostly useless, producing an unnamed subquery rejected by - most databases in any case. The new behavior is modeled after - that of the very successful :meth:`_orm.Query.join` method in the - ORM, in order to support the functionality of :class:`_orm.Query` - being available by using a :class:`_sql.Select` object with an - :class:`_orm.Session`. - - See the notes for this change at :ref:`change_select_join`. - - .. seealso:: - - :ref:`tutorial_select_join` - in the :doc:`/tutorial/index` - - :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` - - :meth:`_expression.Select.join` - - """ - return self.join(target, onclause=onclause, isouter=True, full=full) - - def get_final_froms(self) -> Sequence[FromClause]: - """Compute the final displayed list of :class:`_expression.FromClause` - elements. - - This method will run through the full computation required to - determine what FROM elements will be displayed in the resulting - SELECT statement, including shadowing individual tables with - JOIN objects, as well as full computation for ORM use cases including - eager loading clauses. - - For ORM use, this accessor returns the **post compilation** - list of FROM objects; this collection will include elements such as - eagerly loaded tables and joins. The objects will **not** be - ORM enabled and not work as a replacement for the - :meth:`_sql.Select.select_froms` collection; additionally, the - method is not well performing for an ORM enabled statement as it - will incur the full ORM construction process. - - To retrieve the FROM list that's implied by the "columns" collection - passed to the :class:`_sql.Select` originally, use the - :attr:`_sql.Select.columns_clause_froms` accessor. - - To select from an alternative set of columns while maintaining the - FROM list, use the :meth:`_sql.Select.with_only_columns` method and - pass the - :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter. - - .. versionadded:: 1.4.23 - the :meth:`_sql.Select.get_final_froms` - method replaces the previous :attr:`_sql.Select.froms` accessor, - which is deprecated. - - .. seealso:: - - :attr:`_sql.Select.columns_clause_froms` - - """ - - return self._compile_state_factory(self, None)._get_display_froms() - - @property - @util.deprecated( - "1.4.23", - "The :attr:`_expression.Select.froms` attribute is moved to " - "the :meth:`_expression.Select.get_final_froms` method.", - ) - def froms(self) -> Sequence[FromClause]: - """Return the displayed list of :class:`_expression.FromClause` - elements. - - - """ - return self.get_final_froms() - - @property - def columns_clause_froms(self) -> List[FromClause]: - """Return the set of :class:`_expression.FromClause` objects implied - by the columns clause of this SELECT statement. - - .. versionadded:: 1.4.23 - - .. seealso:: - - :attr:`_sql.Select.froms` - "final" FROM list taking the full - statement into account - - :meth:`_sql.Select.with_only_columns` - makes use of this - collection to set up a new FROM list - - """ - - return SelectState.get_plugin_class(self).get_columns_clause_froms( - self - ) - - @property - def inner_columns(self) -> _SelectIterable: - """An iterator of all :class:`_expression.ColumnElement` - expressions which would - be rendered into the columns clause of the resulting SELECT statement. - - This method is legacy as of 1.4 and is superseded by the - :attr:`_expression.Select.exported_columns` collection. - - """ - - return iter(self._all_selected_columns) - - def is_derived_from(self, fromclause: Optional[FromClause]) -> bool: - if fromclause is not None and self in fromclause._cloned_set: - return True - - for f in self._iterate_from_elements(): - if f.is_derived_from(fromclause): - return True - return False - - def _copy_internals( - self, clone: _CloneCallableType = _clone, **kw: Any - ) -> None: - # Select() object has been cloned and probably adapted by the - # given clone function. Apply the cloning function to internal - # objects - - # 1. keep a dictionary of the froms we've cloned, and what - # they've become. This allows us to ensure the same cloned from - # is used when other items such as columns are "cloned" - - all_the_froms = set( - itertools.chain( - _from_objects(*self._raw_columns), - _from_objects(*self._where_criteria), - _from_objects(*[elem[0] for elem in self._setup_joins]), - ) - ) - - # do a clone for the froms we've gathered. what is important here - # is if any of the things we are selecting from, like tables, - # were converted into Join objects. if so, these need to be - # added to _from_obj explicitly, because otherwise they won't be - # part of the new state, as they don't associate themselves with - # their columns. - new_froms = {f: clone(f, **kw) for f in all_the_froms} - - # 2. copy FROM collections, adding in joins that we've created. - existing_from_obj = [clone(f, **kw) for f in self._from_obj] - add_froms = ( - {f for f in new_froms.values() if isinstance(f, Join)} - .difference(all_the_froms) - .difference(existing_from_obj) - ) - - self._from_obj = tuple(existing_from_obj) + tuple(add_froms) - - # 3. clone everything else, making sure we use columns - # corresponding to the froms we just made. - def replace( - obj: Union[BinaryExpression[Any], ColumnClause[Any]], - **kw: Any, - ) -> Optional[KeyedColumnElement[ColumnElement[Any]]]: - if isinstance(obj, ColumnClause) and obj.table in new_froms: - newelem = new_froms[obj.table].corresponding_column(obj) - return newelem - return None - - kw["replace"] = replace - - # copy everything else. for table-ish things like correlate, - # correlate_except, setup_joins, these clone normally. For - # column-expression oriented things like raw_columns, where_criteria, - # order by, we get this from the new froms. - super()._copy_internals(clone=clone, omit_attrs=("_from_obj",), **kw) - - self._reset_memoizations() - - def get_children(self, **kw: Any) -> Iterable[ClauseElement]: - return itertools.chain( - super().get_children( - omit_attrs=("_from_obj", "_correlate", "_correlate_except"), - **kw, - ), - self._iterate_from_elements(), - ) - - @_generative - def add_columns( - self, *entities: _ColumnsClauseArgument[Any] - ) -> Select[Any]: - r"""Return a new :func:`_expression.select` construct with - the given entities appended to its columns clause. - - E.g.:: - - my_select = my_select.add_columns(table.c.new_column) - - The original expressions in the columns clause remain in place. - To replace the original expressions with new ones, see the method - :meth:`_expression.Select.with_only_columns`. - - :param \*entities: column, table, or other entity expressions to be - added to the columns clause - - .. seealso:: - - :meth:`_expression.Select.with_only_columns` - replaces existing - expressions rather than appending. - - :ref:`orm_queryguide_select_multiple_entities` - ORM-centric - example - - """ - self._reset_memoizations() - - self._raw_columns = self._raw_columns + [ - coercions.expect( - roles.ColumnsClauseRole, column, apply_propagate_attrs=self - ) - for column in entities - ] - return self - - def _set_entities( - self, entities: Iterable[_ColumnsClauseArgument[Any]] - ) -> None: - self._raw_columns = [ - coercions.expect( - roles.ColumnsClauseRole, ent, apply_propagate_attrs=self - ) - for ent in util.to_list(entities) - ] - - @util.deprecated( - "1.4", - "The :meth:`_expression.Select.column` method is deprecated and will " - "be removed in a future release. Please use " - ":meth:`_expression.Select.add_columns`", - ) - def column(self, column: _ColumnsClauseArgument[Any]) -> Select[Any]: - """Return a new :func:`_expression.select` construct with - the given column expression added to its columns clause. - - E.g.:: - - my_select = my_select.column(table.c.new_column) - - See the documentation for - :meth:`_expression.Select.with_only_columns` - for guidelines on adding /replacing the columns of a - :class:`_expression.Select` object. - - """ - return self.add_columns(column) - - @util.preload_module("sqlalchemy.sql.util") - def reduce_columns(self, only_synonyms: bool = True) -> Select[Any]: - """Return a new :func:`_expression.select` construct with redundantly - named, equivalently-valued columns removed from the columns clause. - - "Redundant" here means two columns where one refers to the - other either based on foreign key, or via a simple equality - comparison in the WHERE clause of the statement. The primary purpose - of this method is to automatically construct a select statement - with all uniquely-named columns, without the need to use - table-qualified labels as - :meth:`_expression.Select.set_label_style` - does. - - When columns are omitted based on foreign key, the referred-to - column is the one that's kept. When columns are omitted based on - WHERE equivalence, the first column in the columns clause is the - one that's kept. - - :param only_synonyms: when True, limit the removal of columns - to those which have the same name as the equivalent. Otherwise, - all columns that are equivalent to another are removed. - - """ - woc: Select[Any] - woc = self.with_only_columns( - *util.preloaded.sql_util.reduce_columns( - self._all_selected_columns, - only_synonyms=only_synonyms, - *(self._where_criteria + self._from_obj), - ) - ) - return woc - - # START OVERLOADED FUNCTIONS self.with_only_columns Select 8 - - # code within this block is **programmatically, - # statically generated** by tools/generate_sel_v1_overloads.py - - @overload - def with_only_columns(self, __ent0: _TCCA[_T0]) -> Select[Tuple[_T0]]: ... - - @overload - def with_only_columns( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1] - ) -> Select[Tuple[_T0, _T1]]: ... - - @overload - def with_only_columns( - self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2] - ) -> Select[Tuple[_T0, _T1, _T2]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - ) -> Select[Tuple[_T0, _T1, _T2, _T3]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]: ... - - @overload - def with_only_columns( - self, - __ent0: _TCCA[_T0], - __ent1: _TCCA[_T1], - __ent2: _TCCA[_T2], - __ent3: _TCCA[_T3], - __ent4: _TCCA[_T4], - __ent5: _TCCA[_T5], - __ent6: _TCCA[_T6], - __ent7: _TCCA[_T7], - ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]: ... - - # END OVERLOADED FUNCTIONS self.with_only_columns - - @overload - def with_only_columns( - self, - *entities: _ColumnsClauseArgument[Any], - maintain_column_froms: bool = False, - **__kw: Any, - ) -> Select[Any]: ... - - @_generative - def with_only_columns( - self, - *entities: _ColumnsClauseArgument[Any], - maintain_column_froms: bool = False, - **__kw: Any, - ) -> Select[Any]: - r"""Return a new :func:`_expression.select` construct with its columns - clause replaced with the given entities. - - By default, this method is exactly equivalent to as if the original - :func:`_expression.select` had been called with the given entities. - E.g. a statement:: - - s = select(table1.c.a, table1.c.b) - s = s.with_only_columns(table1.c.b) - - should be exactly equivalent to:: - - s = select(table1.c.b) - - In this mode of operation, :meth:`_sql.Select.with_only_columns` - will also dynamically alter the FROM clause of the - statement if it is not explicitly stated. - To maintain the existing set of FROMs including those implied by the - current columns clause, add the - :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter:: - - s = select(table1.c.a, table2.c.b) - s = s.with_only_columns(table1.c.a, maintain_column_froms=True) - - The above parameter performs a transfer of the effective FROMs - in the columns collection to the :meth:`_sql.Select.select_from` - method, as though the following were invoked:: - - s = select(table1.c.a, table2.c.b) - s = s.select_from(table1, table2).with_only_columns(table1.c.a) - - The :paramref:`_sql.Select.with_only_columns.maintain_column_froms` - parameter makes use of the :attr:`_sql.Select.columns_clause_froms` - collection and performs an operation equivalent to the following:: - - s = select(table1.c.a, table2.c.b) - s = s.select_from(*s.columns_clause_froms).with_only_columns(table1.c.a) - - :param \*entities: column expressions to be used. - - :param maintain_column_froms: boolean parameter that will ensure the - FROM list implied from the current columns clause will be transferred - to the :meth:`_sql.Select.select_from` method first. - - .. versionadded:: 1.4.23 - - """ # noqa: E501 - - if __kw: - raise _no_kw() - - # memoizations should be cleared here as of - # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this - # is the case for now. - self._assert_no_memoizations() - - if maintain_column_froms: - self.select_from.non_generative( # type: ignore - self, *self.columns_clause_froms - ) - - # then memoize the FROMs etc. - _MemoizedSelectEntities._generate_for_statement(self) - - self._raw_columns = [ - coercions.expect(roles.ColumnsClauseRole, c) - for c in coercions._expression_collection_was_a_list( - "entities", "Select.with_only_columns", entities - ) - ] - return self - - @property - def whereclause(self) -> Optional[ColumnElement[Any]]: - """Return the completed WHERE clause for this - :class:`_expression.Select` statement. - - This assembles the current collection of WHERE criteria - into a single :class:`_expression.BooleanClauseList` construct. - - - .. versionadded:: 1.4 - - """ - - return BooleanClauseList._construct_for_whereclause( - self._where_criteria - ) - - _whereclause = whereclause - - @_generative - def where(self, *whereclause: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.select` construct with - the given expression added to - its WHERE clause, joined to the existing clause via AND, if any. - - """ - - assert isinstance(self._where_criteria, tuple) - - for criterion in whereclause: - where_criteria: ColumnElement[Any] = coercions.expect( - roles.WhereHavingRole, criterion, apply_propagate_attrs=self - ) - self._where_criteria += (where_criteria,) - return self - - @_generative - def having(self, *having: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.select` construct with - the given expression added to - its HAVING clause, joined to the existing clause via AND, if any. - - """ - - for criterion in having: - having_criteria = coercions.expect( - roles.WhereHavingRole, criterion, apply_propagate_attrs=self - ) - self._having_criteria += (having_criteria,) - return self - - @_generative - def distinct(self, *expr: _ColumnExpressionArgument[Any]) -> Self: - r"""Return a new :func:`_expression.select` construct which - will apply DISTINCT to its columns clause. - - :param \*expr: optional column expressions. When present, - the PostgreSQL dialect will render a ``DISTINCT ON (<expressions>>)`` - construct. - - .. deprecated:: 1.4 Using \*expr in other dialects is deprecated - and will raise :class:`_exc.CompileError` in a future version. - - """ - if expr: - self._distinct = True - self._distinct_on = self._distinct_on + tuple( - coercions.expect(roles.ByOfRole, e, apply_propagate_attrs=self) - for e in expr - ) - else: - self._distinct = True - return self - - @_generative - def select_from(self, *froms: _FromClauseArgument) -> Self: - r"""Return a new :func:`_expression.select` construct with the - given FROM expression(s) - merged into its list of FROM objects. - - E.g.:: - - table1 = table('t1', column('a')) - table2 = table('t2', column('b')) - s = select(table1.c.a).\ - select_from( - table1.join(table2, table1.c.a==table2.c.b) - ) - - The "from" list is a unique set on the identity of each element, - so adding an already present :class:`_schema.Table` - or other selectable - will have no effect. Passing a :class:`_expression.Join` that refers - to an already present :class:`_schema.Table` - or other selectable will have - the effect of concealing the presence of that selectable as - an individual element in the rendered FROM list, instead - rendering it into a JOIN clause. - - While the typical purpose of :meth:`_expression.Select.select_from` - is to - replace the default, derived FROM clause with a join, it can - also be called with individual table elements, multiple times - if desired, in the case that the FROM clause cannot be fully - derived from the columns clause:: - - select(func.count('*')).select_from(table1) - - """ - - self._from_obj += tuple( - coercions.expect( - roles.FromClauseRole, fromclause, apply_propagate_attrs=self - ) - for fromclause in froms - ) - return self - - @_generative - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.Select` - which will correlate the given FROM - clauses to that of an enclosing :class:`_expression.Select`. - - Calling this method turns off the :class:`_expression.Select` object's - default behavior of "auto-correlation". Normally, FROM elements - which appear in a :class:`_expression.Select` - that encloses this one via - its :term:`WHERE clause`, ORDER BY, HAVING or - :term:`columns clause` will be omitted from this - :class:`_expression.Select` - object's :term:`FROM clause`. - Setting an explicit correlation collection using the - :meth:`_expression.Select.correlate` - method provides a fixed list of FROM objects - that can potentially take place in this process. - - When :meth:`_expression.Select.correlate` - is used to apply specific FROM clauses - for correlation, the FROM elements become candidates for - correlation regardless of how deeply nested this - :class:`_expression.Select` - object is, relative to an enclosing :class:`_expression.Select` - which refers to - the same FROM object. This is in contrast to the behavior of - "auto-correlation" which only correlates to an immediate enclosing - :class:`_expression.Select`. - Multi-level correlation ensures that the link - between enclosed and enclosing :class:`_expression.Select` - is always via - at least one WHERE/ORDER BY/HAVING/columns clause in order for - correlation to take place. - - If ``None`` is passed, the :class:`_expression.Select` - object will correlate - none of its FROM entries, and all will render unconditionally - in the local FROM clause. - - :param \*fromclauses: one or more :class:`.FromClause` or other - FROM-compatible construct such as an ORM mapped entity to become part - of the correlate collection; alternatively pass a single value - ``None`` to remove all existing correlations. - - .. seealso:: - - :meth:`_expression.Select.correlate_except` - - :ref:`tutorial_scalar_subquery` - - """ - - # tests failing when we try to change how these - # arguments are passed - - self._auto_correlate = False - if not fromclauses or fromclauses[0] in {None, False}: - if len(fromclauses) > 1: - raise exc.ArgumentError( - "additional FROM objects not accepted when " - "passing None/False to correlate()" - ) - self._correlate = () - else: - self._correlate = self._correlate + tuple( - coercions.expect(roles.FromClauseRole, f) for f in fromclauses - ) - return self - - @_generative - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.Select` - which will omit the given FROM - clauses from the auto-correlation process. - - Calling :meth:`_expression.Select.correlate_except` turns off the - :class:`_expression.Select` object's default behavior of - "auto-correlation" for the given FROM elements. An element - specified here will unconditionally appear in the FROM list, while - all other FROM elements remain subject to normal auto-correlation - behaviors. - - If ``None`` is passed, or no arguments are passed, - the :class:`_expression.Select` object will correlate all of its - FROM entries. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate-exception collection. - - .. seealso:: - - :meth:`_expression.Select.correlate` - - :ref:`tutorial_scalar_subquery` - - """ - - self._auto_correlate = False - if not fromclauses or fromclauses[0] in {None, False}: - if len(fromclauses) > 1: - raise exc.ArgumentError( - "additional FROM objects not accepted when " - "passing None/False to correlate_except()" - ) - self._correlate_except = () - else: - self._correlate_except = (self._correlate_except or ()) + tuple( - coercions.expect(roles.FromClauseRole, f) for f in fromclauses - ) - - return self - - @HasMemoized_ro_memoized_attribute - def selected_columns( - self, - ) -> ColumnCollection[str, ColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - For a :func:`_expression.select` construct, the collection here is - exactly what would be rendered inside the "SELECT" statement, and the - :class:`_expression.ColumnElement` objects are directly present as they - were given, e.g.:: - - col1 = column('q', Integer) - col2 = column('p', Integer) - stmt = select(col1, col2) - - Above, ``stmt.selected_columns`` would be a collection that contains - the ``col1`` and ``col2`` objects directly. For a statement that is - against a :class:`_schema.Table` or other - :class:`_expression.FromClause`, the collection will use the - :class:`_expression.ColumnElement` objects that are in the - :attr:`_expression.FromClause.c` collection of the from element. - - A use case for the :attr:`_sql.Select.selected_columns` collection is - to allow the existing columns to be referenced when adding additional - criteria, e.g.:: - - def filter_on_id(my_select, id): - return my_select.where(my_select.selected_columns['id'] == id) - - stmt = select(MyModel) - - # adds "WHERE id=:param" to the statement - stmt = filter_on_id(stmt, 42) - - .. note:: - - The :attr:`_sql.Select.selected_columns` collection does not - include expressions established in the columns clause using the - :func:`_sql.text` construct; these are silently omitted from the - collection. To use plain textual column expressions inside of a - :class:`_sql.Select` construct, use the :func:`_sql.literal_column` - construct. - - - .. versionadded:: 1.4 - - """ - - # compare to SelectState._generate_columns_plus_names, which - # generates the actual names used in the SELECT string. that - # method is more complex because it also renders columns that are - # fully ambiguous, e.g. same column more than once. - conv = cast( - "Callable[[Any], str]", - SelectState._column_naming_convention(self._label_style), - ) - - cc: ColumnCollection[str, ColumnElement[Any]] = ColumnCollection( - [ - (conv(c), c) - for c in self._all_selected_columns - if is_column_element(c) - ] - ) - return cc.as_readonly() - - @HasMemoized_ro_memoized_attribute - def _all_selected_columns(self) -> _SelectIterable: - meth = SelectState.get_plugin_class(self).all_selected_columns - return list(meth(self)) - - def _ensure_disambiguated_names(self) -> Select[Any]: - if self._label_style is LABEL_STYLE_NONE: - self = self.set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY) - return self - - def _generate_fromclause_column_proxies( - self, - subquery: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - """Generate column proxies to place in the exported ``.c`` - collection of a subquery.""" - - if proxy_compound_columns: - extra_col_iterator = proxy_compound_columns - prox = [ - c._make_proxy( - subquery, - key=proxy_key, - name=required_label_name, - name_is_truncatable=True, - compound_select_cols=extra_cols, - ) - for ( - ( - required_label_name, - proxy_key, - fallback_label_name, - c, - repeated, - ), - extra_cols, - ) in ( - zip( - self._generate_columns_plus_names(False), - extra_col_iterator, - ) - ) - if is_column_element(c) - ] - else: - prox = [ - c._make_proxy( - subquery, - key=proxy_key, - name=required_label_name, - name_is_truncatable=True, - ) - for ( - required_label_name, - proxy_key, - fallback_label_name, - c, - repeated, - ) in (self._generate_columns_plus_names(False)) - if is_column_element(c) - ] - - subquery._columns._populate_separate_keys(prox) - - def _needs_parens_for_grouping(self) -> bool: - return self._has_row_limiting_clause or bool( - self._order_by_clause.clauses - ) - - def self_group( - self, against: Optional[OperatorType] = None - ) -> Union[SelectStatementGrouping[Self], Self]: - ... - """Return a 'grouping' construct as per the - :class:`_expression.ClauseElement` specification. - - This produces an element that can be embedded in an expression. Note - that this method is called automatically as needed when constructing - expressions and should not require explicit use. - - """ - if ( - isinstance(against, CompoundSelect) - and not self._needs_parens_for_grouping() - ): - return self - else: - return SelectStatementGrouping(self) - - def union( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``UNION`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_union(self, *other) - - def union_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``UNION ALL`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_union_all(self, *other) - - def except_( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``EXCEPT`` of this select() construct against - the given selectable provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - """ - return CompoundSelect._create_except(self, *other) - - def except_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``EXCEPT ALL`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - """ - return CompoundSelect._create_except_all(self, *other) - - def intersect( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``INTERSECT`` of this select() construct against - the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_intersect(self, *other) - - def intersect_all( - self, *other: _SelectStatementForCompoundArgument - ) -> CompoundSelect: - r"""Return a SQL ``INTERSECT ALL`` of this select() construct - against the given selectables provided as positional arguments. - - :param \*other: one or more elements with which to create a - UNION. - - .. versionchanged:: 1.4.28 - - multiple elements are now accepted. - - :param \**kwargs: keyword arguments are forwarded to the constructor - for the newly created :class:`_sql.CompoundSelect` object. - - """ - return CompoundSelect._create_intersect_all(self, *other) - - -class ScalarSelect( - roles.InElementRole, Generative, GroupedElement, ColumnElement[_T] -): - """Represent a scalar subquery. - - - A :class:`_sql.ScalarSelect` is created by invoking the - :meth:`_sql.SelectBase.scalar_subquery` method. The object - then participates in other SQL expressions as a SQL column expression - within the :class:`_sql.ColumnElement` hierarchy. - - .. seealso:: - - :meth:`_sql.SelectBase.scalar_subquery` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - """ - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("type", InternalTraversal.dp_type), - ] - - _from_objects: List[FromClause] = [] - _is_from_container = True - if not TYPE_CHECKING: - _is_implicitly_boolean = False - inherit_cache = True - - element: SelectBase - - def __init__(self, element: SelectBase) -> None: - self.element = element - self.type = element._scalar_type() - self._propagate_attrs = element._propagate_attrs - - def __getattr__(self, attr: str) -> Any: - return getattr(self.element, attr) - - def __getstate__(self) -> Dict[str, Any]: - return {"element": self.element, "type": self.type} - - def __setstate__(self, state: Dict[str, Any]) -> None: - self.element = state["element"] - self.type = state["type"] - - @property - def columns(self) -> NoReturn: - raise exc.InvalidRequestError( - "Scalar Select expression has no " - "columns; use this object directly " - "within a column-level expression." - ) - - c = columns - - @_generative - def where(self, crit: _ColumnExpressionArgument[bool]) -> Self: - """Apply a WHERE clause to the SELECT statement referred to - by this :class:`_expression.ScalarSelect`. - - """ - self.element = cast("Select[Any]", self.element).where(crit) - return self - - @overload - def self_group( - self: ScalarSelect[Any], against: Optional[OperatorType] = None - ) -> ScalarSelect[Any]: ... - - @overload - def self_group( - self: ColumnElement[Any], against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: ... - - def self_group( - self, against: Optional[OperatorType] = None - ) -> ColumnElement[Any]: - return self - - if TYPE_CHECKING: - - def _ungroup(self) -> Select[Any]: ... - - @_generative - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.ScalarSelect` - which will correlate the given FROM - clauses to that of an enclosing :class:`_expression.Select`. - - This method is mirrored from the :meth:`_sql.Select.correlate` method - of the underlying :class:`_sql.Select`. The method applies the - :meth:_sql.Select.correlate` method, then returns a new - :class:`_sql.ScalarSelect` against that statement. - - .. versionadded:: 1.4 Previously, the - :meth:`_sql.ScalarSelect.correlate` - method was only available from :class:`_sql.Select`. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate collection. - - .. seealso:: - - :meth:`_expression.ScalarSelect.correlate_except` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - - """ - self.element = cast("Select[Any]", self.element).correlate( - *fromclauses - ) - return self - - @_generative - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - r"""Return a new :class:`_expression.ScalarSelect` - which will omit the given FROM - clauses from the auto-correlation process. - - This method is mirrored from the - :meth:`_sql.Select.correlate_except` method of the underlying - :class:`_sql.Select`. The method applies the - :meth:_sql.Select.correlate_except` method, then returns a new - :class:`_sql.ScalarSelect` against that statement. - - .. versionadded:: 1.4 Previously, the - :meth:`_sql.ScalarSelect.correlate_except` - method was only available from :class:`_sql.Select`. - - :param \*fromclauses: a list of one or more - :class:`_expression.FromClause` - constructs, or other compatible constructs (i.e. ORM-mapped - classes) to become part of the correlate-exception collection. - - .. seealso:: - - :meth:`_expression.ScalarSelect.correlate` - - :ref:`tutorial_scalar_subquery` - in the 2.0 tutorial - - - """ - - self.element = cast("Select[Any]", self.element).correlate_except( - *fromclauses - ) - return self - - -class Exists(UnaryExpression[bool]): - """Represent an ``EXISTS`` clause. - - See :func:`_sql.exists` for a description of usage. - - An ``EXISTS`` clause can also be constructed from a :func:`_sql.select` - instance by calling :meth:`_sql.SelectBase.exists`. - - """ - - inherit_cache = True - element: Union[SelectStatementGrouping[Select[Any]], ScalarSelect[Any]] - - def __init__( - self, - __argument: Optional[ - Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]] - ] = None, - ): - s: ScalarSelect[Any] - - # TODO: this seems like we should be using coercions for this - if __argument is None: - s = Select(literal_column("*")).scalar_subquery() - elif isinstance(__argument, SelectBase): - s = __argument.scalar_subquery() - s._propagate_attrs = __argument._propagate_attrs - elif isinstance(__argument, ScalarSelect): - s = __argument - else: - s = Select(__argument).scalar_subquery() - - UnaryExpression.__init__( - self, - s, - operator=operators.exists, - type_=type_api.BOOLEANTYPE, - wraps_column_expression=True, - ) - - @util.ro_non_memoized_property - def _from_objects(self) -> List[FromClause]: - return [] - - def _regroup( - self, fn: Callable[[Select[Any]], Select[Any]] - ) -> SelectStatementGrouping[Select[Any]]: - element = self.element._ungroup() - new_element = fn(element) - - return_value = new_element.self_group(against=operators.exists) - assert isinstance(return_value, SelectStatementGrouping) - return return_value - - def select(self) -> Select[Any]: - r"""Return a SELECT of this :class:`_expression.Exists`. - - e.g.:: - - stmt = exists(some_table.c.id).where(some_table.c.id == 5).select() - - This will produce a statement resembling:: - - SELECT EXISTS (SELECT id FROM some_table WHERE some_table = :param) AS anon_1 - - .. seealso:: - - :func:`_expression.select` - general purpose - method which allows for arbitrary column lists. - - """ # noqa - - return Select(self) - - def correlate( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - """Apply correlation to the subquery noted by this - :class:`_sql.Exists`. - - .. seealso:: - - :meth:`_sql.ScalarSelect.correlate` - - """ - e = self._clone() - e.element = self._regroup( - lambda element: element.correlate(*fromclauses) - ) - return e - - def correlate_except( - self, - *fromclauses: Union[Literal[None, False], _FromClauseArgument], - ) -> Self: - """Apply correlation to the subquery noted by this - :class:`_sql.Exists`. - - .. seealso:: - - :meth:`_sql.ScalarSelect.correlate_except` - - """ - - e = self._clone() - e.element = self._regroup( - lambda element: element.correlate_except(*fromclauses) - ) - return e - - def select_from(self, *froms: _FromClauseArgument) -> Self: - """Return a new :class:`_expression.Exists` construct, - applying the given - expression to the :meth:`_expression.Select.select_from` - method of the select - statement contained. - - .. note:: it is typically preferable to build a :class:`_sql.Select` - statement first, including the desired WHERE clause, then use the - :meth:`_sql.SelectBase.exists` method to produce an - :class:`_sql.Exists` object at once. - - """ - e = self._clone() - e.element = self._regroup(lambda element: element.select_from(*froms)) - return e - - def where(self, *clause: _ColumnExpressionArgument[bool]) -> Self: - """Return a new :func:`_expression.exists` construct with the - given expression added to - its WHERE clause, joined to the existing clause via AND, if any. - - - .. note:: it is typically preferable to build a :class:`_sql.Select` - statement first, including the desired WHERE clause, then use the - :meth:`_sql.SelectBase.exists` method to produce an - :class:`_sql.Exists` object at once. - - """ - e = self._clone() - e.element = self._regroup(lambda element: element.where(*clause)) - return e - - -class TextualSelect(SelectBase, ExecutableReturnsRows, Generative): - """Wrap a :class:`_expression.TextClause` construct within a - :class:`_expression.SelectBase` - interface. - - This allows the :class:`_expression.TextClause` object to gain a - ``.c`` collection - and other FROM-like capabilities such as - :meth:`_expression.FromClause.alias`, - :meth:`_expression.SelectBase.cte`, etc. - - The :class:`_expression.TextualSelect` construct is produced via the - :meth:`_expression.TextClause.columns` - method - see that method for details. - - .. versionchanged:: 1.4 the :class:`_expression.TextualSelect` - class was renamed - from ``TextAsFrom``, to more correctly suit its role as a - SELECT-oriented object and not a FROM clause. - - .. seealso:: - - :func:`_expression.text` - - :meth:`_expression.TextClause.columns` - primary creation interface. - - """ - - __visit_name__ = "textual_select" - - _label_style = LABEL_STYLE_NONE - - _traverse_internals: _TraverseInternalsType = [ - ("element", InternalTraversal.dp_clauseelement), - ("column_args", InternalTraversal.dp_clauseelement_list), - ] + SupportsCloneAnnotations._clone_annotations_traverse_internals - - _is_textual = True - - is_text = True - is_select = True - - def __init__( - self, - text: TextClause, - columns: List[_ColumnExpressionArgument[Any]], - positional: bool = False, - ) -> None: - self._init( - text, - # convert for ORM attributes->columns, etc - [ - coercions.expect(roles.LabeledColumnExprRole, c) - for c in columns - ], - positional, - ) - - def _init( - self, - text: TextClause, - columns: List[NamedColumn[Any]], - positional: bool = False, - ) -> None: - self.element = text - self.column_args = columns - self.positional = positional - - @HasMemoized_ro_memoized_attribute - def selected_columns( - self, - ) -> ColumnCollection[str, KeyedColumnElement[Any]]: - """A :class:`_expression.ColumnCollection` - representing the columns that - this SELECT statement or similar construct returns in its result set, - not including :class:`_sql.TextClause` constructs. - - This collection differs from the :attr:`_expression.FromClause.columns` - collection of a :class:`_expression.FromClause` in that the columns - within this collection cannot be directly nested inside another SELECT - statement; a subquery must be applied first which provides for the - necessary parenthesization required by SQL. - - For a :class:`_expression.TextualSelect` construct, the collection - contains the :class:`_expression.ColumnElement` objects that were - passed to the constructor, typically via the - :meth:`_expression.TextClause.columns` method. - - - .. versionadded:: 1.4 - - """ - return ColumnCollection( - (c.key, c) for c in self.column_args - ).as_readonly() - - @util.ro_non_memoized_property - def _all_selected_columns(self) -> _SelectIterable: - return self.column_args - - def set_label_style(self, style: SelectLabelStyle) -> TextualSelect: - return self - - def _ensure_disambiguated_names(self) -> TextualSelect: - return self - - @_generative - def bindparams( - self, - *binds: BindParameter[Any], - **bind_as_values: Any, - ) -> Self: - self.element = self.element.bindparams(*binds, **bind_as_values) - return self - - def _generate_fromclause_column_proxies( - self, - fromclause: FromClause, - *, - proxy_compound_columns: Optional[ - Iterable[Sequence[ColumnElement[Any]]] - ] = None, - ) -> None: - if TYPE_CHECKING: - assert isinstance(fromclause, Subquery) - - if proxy_compound_columns: - fromclause._columns._populate_separate_keys( - c._make_proxy(fromclause, compound_select_cols=extra_cols) - for c, extra_cols in zip( - self.column_args, proxy_compound_columns - ) - ) - else: - fromclause._columns._populate_separate_keys( - c._make_proxy(fromclause) for c in self.column_args - ) - - def _scalar_type(self) -> Union[TypeEngine[Any], Any]: - return self.column_args[0].type - - -TextAsFrom = TextualSelect -"""Backwards compatibility with the previous name""" - - -class AnnotatedFromClause(Annotated): - def _copy_internals(self, **kw: Any) -> None: - super()._copy_internals(**kw) - if kw.get("ind_cols_on_fromclause", False): - ee = self._Annotated__element # type: ignore - - self.c = ee.__class__.c.fget(self) # type: ignore - - @util.ro_memoized_property - def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]: - """proxy the .c collection of the underlying FromClause. - - Originally implemented in 2008 as a simple load of the .c collection - when the annotated construct was created (see d3621ae961a), in modern - SQLAlchemy versions this can be expensive for statements constructed - with ORM aliases. So for #8796 SQLAlchemy 2.0 we instead proxy - it, which works just as well. - - Two different use cases seem to require the collection either copied - from the underlying one, or unique to this AnnotatedFromClause. - - See test_selectable->test_annotated_corresponding_column - - """ - ee = self._Annotated__element # type: ignore - return ee.c # type: ignore diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/sqltypes.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/sqltypes.py deleted file mode 100644 index 1af3c5e..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/sqltypes.py +++ /dev/null @@ -1,3786 +0,0 @@ -# sql/sqltypes.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""SQL specific types. - -""" -from __future__ import annotations - -import collections.abc as collections_abc -import datetime as dt -import decimal -import enum -import json -import pickle -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import List -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 typing import Union -from uuid import UUID as _python_UUID - -from . import coercions -from . import elements -from . import operators -from . import roles -from . import type_api -from .base import _NONE_NAME -from .base import NO_ARG -from .base import SchemaEventTarget -from .cache_key import HasCacheKey -from .elements import quoted_name -from .elements import Slice -from .elements import TypeCoerce as type_coerce # noqa -from .type_api import Emulated -from .type_api import NativeForEmulated # noqa -from .type_api import to_instance as to_instance -from .type_api import TypeDecorator as TypeDecorator -from .type_api import TypeEngine as TypeEngine -from .type_api import TypeEngineMixin -from .type_api import Variant # noqa -from .visitors import InternalTraversal -from .. import event -from .. import exc -from .. import inspection -from .. import util -from ..engine import processors -from ..util import langhelpers -from ..util import OrderedDict -from ..util.typing import is_literal -from ..util.typing import Literal -from ..util.typing import typing_get_args - -if TYPE_CHECKING: - from ._typing import _ColumnExpressionArgument - from ._typing import _TypeEngineArgument - from .operators import OperatorType - from .schema import MetaData - from .type_api import _BindProcessorType - from .type_api import _ComparatorFactory - from .type_api import _MatchedOnType - from .type_api import _ResultProcessorType - from ..engine.interfaces import Dialect - -_T = TypeVar("_T", bound="Any") -_CT = TypeVar("_CT", bound=Any) -_TE = TypeVar("_TE", bound="TypeEngine[Any]") - - -class HasExpressionLookup(TypeEngineMixin): - """Mixin expression adaptations based on lookup tables. - - These rules are currently used by the numeric, integer and date types - which have detailed cross-expression coercion rules. - - """ - - @property - def _expression_adaptations(self): - raise NotImplementedError() - - class Comparator(TypeEngine.Comparator[_CT]): - __slots__ = () - - _blank_dict = util.EMPTY_DICT - - def _adapt_expression( - self, - op: OperatorType, - other_comparator: TypeEngine.Comparator[Any], - ) -> Tuple[OperatorType, TypeEngine[Any]]: - othertype = other_comparator.type._type_affinity - if TYPE_CHECKING: - assert isinstance(self.type, HasExpressionLookup) - lookup = self.type._expression_adaptations.get( - op, self._blank_dict - ).get(othertype, self.type) - if lookup is othertype: - return (op, other_comparator.type) - elif lookup is self.type._type_affinity: - return (op, self.type) - else: - return (op, to_instance(lookup)) - - comparator_factory: _ComparatorFactory[Any] = Comparator - - -class Concatenable(TypeEngineMixin): - """A mixin that marks a type as supporting 'concatenation', - typically strings.""" - - class Comparator(TypeEngine.Comparator[_T]): - __slots__ = () - - def _adapt_expression( - self, - op: OperatorType, - other_comparator: TypeEngine.Comparator[Any], - ) -> Tuple[OperatorType, TypeEngine[Any]]: - if op is operators.add and isinstance( - other_comparator, - (Concatenable.Comparator, NullType.Comparator), - ): - return operators.concat_op, self.expr.type - else: - return super()._adapt_expression(op, other_comparator) - - comparator_factory: _ComparatorFactory[Any] = Comparator - - -class Indexable(TypeEngineMixin): - """A mixin that marks a type as supporting indexing operations, - such as array or JSON structures. - - """ - - class Comparator(TypeEngine.Comparator[_T]): - __slots__ = () - - def _setup_getitem(self, index): - raise NotImplementedError() - - def __getitem__(self, index): - ( - adjusted_op, - adjusted_right_expr, - result_type, - ) = self._setup_getitem(index) - return self.operate( - adjusted_op, adjusted_right_expr, result_type=result_type - ) - - comparator_factory: _ComparatorFactory[Any] = Comparator - - -class String(Concatenable, TypeEngine[str]): - """The base for all string and character types. - - In SQL, corresponds to VARCHAR. - - The `length` field is usually required when the `String` type is - used within a CREATE TABLE statement, as VARCHAR requires a length - on most databases. - - """ - - __visit_name__ = "string" - - def __init__( - self, - length: Optional[int] = None, - collation: Optional[str] = None, - ): - """ - Create a string-holding type. - - :param length: optional, a length for the column for use in - DDL and CAST expressions. May be safely omitted if no ``CREATE - TABLE`` will be issued. Certain databases may require a - ``length`` for use in DDL, and will raise an exception when - the ``CREATE TABLE`` DDL is issued if a ``VARCHAR`` - with no length is included. Whether the value is - interpreted as bytes or characters is database specific. - - :param collation: Optional, a column-level collation for - use in DDL and CAST expressions. Renders using the - COLLATE keyword supported by SQLite, MySQL, and PostgreSQL. - E.g.: - - .. sourcecode:: pycon+sql - - >>> from sqlalchemy import cast, select, String - >>> print(select(cast('some string', String(collation='utf8')))) - {printsql}SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1 - - .. note:: - - In most cases, the :class:`.Unicode` or :class:`.UnicodeText` - datatypes should be used for a :class:`_schema.Column` that expects - to store non-ascii data. These datatypes will ensure that the - correct types are used on the database. - - """ - - self.length = length - self.collation = collation - - def _resolve_for_literal(self, value): - # I was SO PROUD of my regex trick, but we dont need it. - # re.search(r"[^\u0000-\u007F]", value) - - if value.isascii(): - return _STRING - else: - return _UNICODE - - def literal_processor(self, dialect): - def process(value): - value = value.replace("'", "''") - - if dialect.identifier_preparer._double_percents: - value = value.replace("%", "%%") - - return "'%s'" % value - - return process - - def bind_processor(self, dialect): - return None - - def result_processor(self, dialect, coltype): - return None - - @property - def python_type(self): - return str - - def get_dbapi_type(self, dbapi): - return dbapi.STRING - - -class Text(String): - """A variably sized string type. - - In SQL, usually corresponds to CLOB or TEXT. In general, TEXT objects - do not have a length; while some databases will accept a length - argument here, it will be rejected by others. - - """ - - __visit_name__ = "text" - - -class Unicode(String): - """A variable length Unicode string type. - - The :class:`.Unicode` type is a :class:`.String` subclass that assumes - input and output strings that may contain non-ASCII characters, and for - some backends implies an underlying column type that is explicitly - supporting of non-ASCII data, such as ``NVARCHAR`` on Oracle and SQL - Server. This will impact the output of ``CREATE TABLE`` statements and - ``CAST`` functions at the dialect level. - - The character encoding used by the :class:`.Unicode` type that is used to - transmit and receive data to the database is usually determined by the - DBAPI itself. All modern DBAPIs accommodate non-ASCII strings but may have - different methods of managing database encodings; if necessary, this - encoding should be configured as detailed in the notes for the target DBAPI - in the :ref:`dialect_toplevel` section. - - In modern SQLAlchemy, use of the :class:`.Unicode` datatype does not - imply any encoding/decoding behavior within SQLAlchemy itself. In Python - 3, all string objects are inherently Unicode capable, and SQLAlchemy - does not produce bytestring objects nor does it accommodate a DBAPI that - does not return Python Unicode objects in result sets for string values. - - .. warning:: Some database backends, particularly SQL Server with pyodbc, - are known to have undesirable behaviors regarding data that is noted - as being of ``NVARCHAR`` type as opposed to ``VARCHAR``, including - datatype mismatch errors and non-use of indexes. See the section - on :meth:`.DialectEvents.do_setinputsizes` for background on working - around unicode character issues for backends like SQL Server with - pyodbc as well as cx_Oracle. - - .. seealso:: - - :class:`.UnicodeText` - unlengthed textual counterpart - to :class:`.Unicode`. - - :meth:`.DialectEvents.do_setinputsizes` - - - """ - - __visit_name__ = "unicode" - - -class UnicodeText(Text): - """An unbounded-length Unicode string type. - - See :class:`.Unicode` for details on the unicode - behavior of this object. - - Like :class:`.Unicode`, usage the :class:`.UnicodeText` type implies a - unicode-capable type being used on the backend, such as - ``NCLOB``, ``NTEXT``. - - """ - - __visit_name__ = "unicode_text" - - -class Integer(HasExpressionLookup, TypeEngine[int]): - """A type for ``int`` integers.""" - - __visit_name__ = "integer" - - if TYPE_CHECKING: - - @util.ro_memoized_property - def _type_affinity(self) -> Type[Integer]: ... - - def get_dbapi_type(self, dbapi): - return dbapi.NUMBER - - @property - def python_type(self): - return int - - def _resolve_for_literal(self, value): - if value.bit_length() >= 32: - return _BIGINTEGER - else: - return self - - def literal_processor(self, dialect): - def process(value): - return str(int(value)) - - return process - - @util.memoized_property - def _expression_adaptations(self): - return { - operators.add: { - Date: Date, - Integer: self.__class__, - Numeric: Numeric, - }, - operators.mul: { - Interval: Interval, - Integer: self.__class__, - Numeric: Numeric, - }, - operators.truediv: {Integer: Numeric, Numeric: Numeric}, - operators.floordiv: {Integer: self.__class__, Numeric: Numeric}, - operators.sub: {Integer: self.__class__, Numeric: Numeric}, - } - - -class SmallInteger(Integer): - """A type for smaller ``int`` integers. - - Typically generates a ``SMALLINT`` in DDL, and otherwise acts like - a normal :class:`.Integer` on the Python side. - - """ - - __visit_name__ = "small_integer" - - -class BigInteger(Integer): - """A type for bigger ``int`` integers. - - Typically generates a ``BIGINT`` in DDL, and otherwise acts like - a normal :class:`.Integer` on the Python side. - - """ - - __visit_name__ = "big_integer" - - -_N = TypeVar("_N", bound=Union[decimal.Decimal, float]) - - -class Numeric(HasExpressionLookup, TypeEngine[_N]): - """Base for non-integer numeric types, such as - ``NUMERIC``, ``FLOAT``, ``DECIMAL``, and other variants. - - The :class:`.Numeric` datatype when used directly will render DDL - corresponding to precision numerics if available, such as - ``NUMERIC(precision, scale)``. The :class:`.Float` subclass will - attempt to render a floating-point datatype such as ``FLOAT(precision)``. - - :class:`.Numeric` returns Python ``decimal.Decimal`` objects by default, - based on the default value of ``True`` for the - :paramref:`.Numeric.asdecimal` parameter. If this parameter is set to - False, returned values are coerced to Python ``float`` objects. - - The :class:`.Float` subtype, being more specific to floating point, - defaults the :paramref:`.Float.asdecimal` flag to False so that the - default Python datatype is ``float``. - - .. note:: - - When using a :class:`.Numeric` datatype against a database type that - returns Python floating point values to the driver, the accuracy of the - decimal conversion indicated by :paramref:`.Numeric.asdecimal` may be - limited. The behavior of specific numeric/floating point datatypes - is a product of the SQL datatype in use, the Python :term:`DBAPI` - in use, as well as strategies that may be present within - the SQLAlchemy dialect in use. Users requiring specific precision/ - scale are encouraged to experiment with the available datatypes - in order to determine the best results. - - """ - - __visit_name__ = "numeric" - - if TYPE_CHECKING: - - @util.ro_memoized_property - def _type_affinity(self) -> Type[Numeric[_N]]: ... - - _default_decimal_return_scale = 10 - - @overload - def __init__( - self: Numeric[decimal.Decimal], - precision: Optional[int] = ..., - scale: Optional[int] = ..., - decimal_return_scale: Optional[int] = ..., - asdecimal: Literal[True] = ..., - ): ... - - @overload - def __init__( - self: Numeric[float], - precision: Optional[int] = ..., - scale: Optional[int] = ..., - decimal_return_scale: Optional[int] = ..., - asdecimal: Literal[False] = ..., - ): ... - - def __init__( - self, - precision: Optional[int] = None, - scale: Optional[int] = None, - decimal_return_scale: Optional[int] = None, - asdecimal: bool = True, - ): - """ - Construct a Numeric. - - :param precision: the numeric precision for use in DDL ``CREATE - TABLE``. - - :param scale: the numeric scale for use in DDL ``CREATE TABLE``. - - :param asdecimal: default True. Return whether or not - values should be sent as Python Decimal objects, or - as floats. Different DBAPIs send one or the other based on - datatypes - the Numeric type will ensure that return values - are one or the other across DBAPIs consistently. - - :param decimal_return_scale: Default scale to use when converting - from floats to Python decimals. Floating point values will typically - be much longer due to decimal inaccuracy, and most floating point - database types don't have a notion of "scale", so by default the - float type looks for the first ten decimal places when converting. - Specifying this value will override that length. Types which - do include an explicit ".scale" value, such as the base - :class:`.Numeric` as well as the MySQL float types, will use the - value of ".scale" as the default for decimal_return_scale, if not - otherwise specified. - - When using the ``Numeric`` type, care should be taken to ensure - that the asdecimal setting is appropriate for the DBAPI in use - - when Numeric applies a conversion from Decimal->float or float-> - Decimal, this conversion incurs an additional performance overhead - for all result columns received. - - DBAPIs that return Decimal natively (e.g. psycopg2) will have - better accuracy and higher performance with a setting of ``True``, - as the native translation to Decimal reduces the amount of floating- - point issues at play, and the Numeric type itself doesn't need - to apply any further conversions. However, another DBAPI which - returns floats natively *will* incur an additional conversion - overhead, and is still subject to floating point data loss - in - which case ``asdecimal=False`` will at least remove the extra - conversion overhead. - - """ - self.precision = precision - self.scale = scale - self.decimal_return_scale = decimal_return_scale - self.asdecimal = asdecimal - - @property - def _effective_decimal_return_scale(self): - if self.decimal_return_scale is not None: - return self.decimal_return_scale - elif getattr(self, "scale", None) is not None: - return self.scale - else: - return self._default_decimal_return_scale - - def get_dbapi_type(self, dbapi): - return dbapi.NUMBER - - def literal_processor(self, dialect): - def process(value): - return str(value) - - return process - - @property - def python_type(self): - if self.asdecimal: - return decimal.Decimal - else: - return float - - def bind_processor(self, dialect): - if dialect.supports_native_decimal: - return None - else: - return processors.to_float - - def result_processor(self, dialect, coltype): - if self.asdecimal: - if dialect.supports_native_decimal: - # we're a "numeric", DBAPI will give us Decimal directly - return None - else: - # we're a "numeric", DBAPI returns floats, convert. - return processors.to_decimal_processor_factory( - decimal.Decimal, - ( - self.scale - if self.scale is not None - else self._default_decimal_return_scale - ), - ) - else: - if dialect.supports_native_decimal: - return processors.to_float - else: - return None - - @util.memoized_property - def _expression_adaptations(self): - return { - operators.mul: { - Interval: Interval, - Numeric: self.__class__, - Integer: self.__class__, - }, - operators.truediv: { - Numeric: self.__class__, - Integer: self.__class__, - }, - operators.add: {Numeric: self.__class__, Integer: self.__class__}, - operators.sub: {Numeric: self.__class__, Integer: self.__class__}, - } - - -class Float(Numeric[_N]): - """Type representing floating point types, such as ``FLOAT`` or ``REAL``. - - This type returns Python ``float`` objects by default, unless the - :paramref:`.Float.asdecimal` flag is set to ``True``, in which case they - are coerced to ``decimal.Decimal`` objects. - - When a :paramref:`.Float.precision` is not provided in a - :class:`_types.Float` type some backend may compile this type as - an 8 bytes / 64 bit float datatype. To use a 4 bytes / 32 bit float - datatype a precision <= 24 can usually be provided or the - :class:`_types.REAL` type can be used. - This is known to be the case in the PostgreSQL and MSSQL dialects - that render the type as ``FLOAT`` that's in both an alias of - ``DOUBLE PRECISION``. Other third party dialects may have similar - behavior. - """ - - __visit_name__ = "float" - - scale = None - - @overload - def __init__( - self: Float[float], - precision: Optional[int] = ..., - asdecimal: Literal[False] = ..., - decimal_return_scale: Optional[int] = ..., - ): ... - - @overload - def __init__( - self: Float[decimal.Decimal], - precision: Optional[int] = ..., - asdecimal: Literal[True] = ..., - decimal_return_scale: Optional[int] = ..., - ): ... - - def __init__( - self: Float[_N], - precision: Optional[int] = None, - asdecimal: bool = False, - decimal_return_scale: Optional[int] = None, - ): - r""" - Construct a Float. - - :param precision: the numeric precision for use in DDL ``CREATE - TABLE``. Backends **should** attempt to ensure this precision - indicates a number of digits for the generic - :class:`_sqltypes.Float` datatype. - - .. note:: For the Oracle backend, the - :paramref:`_sqltypes.Float.precision` parameter is not accepted - when rendering DDL, as Oracle does not support float precision - specified as a number of decimal places. Instead, use the - Oracle-specific :class:`_oracle.FLOAT` datatype and specify the - :paramref:`_oracle.FLOAT.binary_precision` parameter. This is new - in version 2.0 of SQLAlchemy. - - To create a database agnostic :class:`_types.Float` that - separately specifies binary precision for Oracle, use - :meth:`_types.TypeEngine.with_variant` as follows:: - - from sqlalchemy import Column - from sqlalchemy import Float - from sqlalchemy.dialects import oracle - - Column( - "float_data", - Float(5).with_variant(oracle.FLOAT(binary_precision=16), "oracle") - ) - - :param asdecimal: the same flag as that of :class:`.Numeric`, but - defaults to ``False``. Note that setting this flag to ``True`` - results in floating point conversion. - - :param decimal_return_scale: Default scale to use when converting - from floats to Python decimals. Floating point values will typically - be much longer due to decimal inaccuracy, and most floating point - database types don't have a notion of "scale", so by default the - float type looks for the first ten decimal places when converting. - Specifying this value will override that length. Note that the - MySQL float types, which do include "scale", will use "scale" - as the default for decimal_return_scale, if not otherwise specified. - - """ # noqa: E501 - self.precision = precision - self.asdecimal = asdecimal - self.decimal_return_scale = decimal_return_scale - - def result_processor(self, dialect, coltype): - if self.asdecimal: - return processors.to_decimal_processor_factory( - decimal.Decimal, self._effective_decimal_return_scale - ) - elif dialect.supports_native_decimal: - return processors.to_float - else: - return None - - -class Double(Float[_N]): - """A type for double ``FLOAT`` floating point types. - - Typically generates a ``DOUBLE`` or ``DOUBLE_PRECISION`` in DDL, - and otherwise acts like a normal :class:`.Float` on the Python - side. - - .. versionadded:: 2.0 - - """ - - __visit_name__ = "double" - - -class _RenderISO8601NoT: - def _literal_processor_datetime(self, dialect): - return self._literal_processor_portion(dialect, None) - - def _literal_processor_date(self, dialect): - return self._literal_processor_portion(dialect, 0) - - def _literal_processor_time(self, dialect): - return self._literal_processor_portion(dialect, -1) - - def _literal_processor_portion(self, dialect, _portion=None): - assert _portion in (None, 0, -1) - if _portion is not None: - - def process(value): - return f"""'{value.isoformat().split("T")[_portion]}'""" - - else: - - def process(value): - return f"""'{value.isoformat().replace("T", " ")}'""" - - return process - - -class DateTime( - _RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.datetime] -): - """A type for ``datetime.datetime()`` objects. - - Date and time types return objects from the Python ``datetime`` - module. Most DBAPIs have built in support for the datetime - module, with the noted exception of SQLite. In the case of - SQLite, date and time types are stored as strings which are then - converted back to datetime objects when rows are returned. - - For the time representation within the datetime type, some - backends include additional options, such as timezone support and - fractional seconds support. For fractional seconds, use the - dialect-specific datatype, such as :class:`.mysql.TIME`. For - timezone support, use at least the :class:`_types.TIMESTAMP` datatype, - if not the dialect-specific datatype object. - - """ - - __visit_name__ = "datetime" - - def __init__(self, timezone: bool = False): - """Construct a new :class:`.DateTime`. - - :param timezone: boolean. Indicates that the datetime type should - enable timezone support, if available on the - **base date/time-holding type only**. It is recommended - to make use of the :class:`_types.TIMESTAMP` datatype directly when - using this flag, as some databases include separate generic - date/time-holding types distinct from the timezone-capable - TIMESTAMP datatype, such as Oracle. - - - """ - self.timezone = timezone - - def get_dbapi_type(self, dbapi): - return dbapi.DATETIME - - def _resolve_for_literal(self, value): - with_timezone = value.tzinfo is not None - if with_timezone and not self.timezone: - return DATETIME_TIMEZONE - else: - return self - - def literal_processor(self, dialect): - return self._literal_processor_datetime(dialect) - - @property - def python_type(self): - return dt.datetime - - @util.memoized_property - def _expression_adaptations(self): - # Based on - # https://www.postgresql.org/docs/current/static/functions-datetime.html. - - return { - operators.add: {Interval: self.__class__}, - operators.sub: {Interval: self.__class__, DateTime: Interval}, - } - - -class Date(_RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.date]): - """A type for ``datetime.date()`` objects.""" - - __visit_name__ = "date" - - def get_dbapi_type(self, dbapi): - return dbapi.DATETIME - - @property - def python_type(self): - return dt.date - - def literal_processor(self, dialect): - return self._literal_processor_date(dialect) - - @util.memoized_property - def _expression_adaptations(self): - # Based on - # https://www.postgresql.org/docs/current/static/functions-datetime.html. - - return { - operators.add: { - Integer: self.__class__, - Interval: DateTime, - Time: DateTime, - }, - operators.sub: { - # date - integer = date - Integer: self.__class__, - # date - date = integer. - Date: Integer, - Interval: DateTime, - # date - datetime = interval, - # this one is not in the PG docs - # but works - DateTime: Interval, - }, - } - - -class Time(_RenderISO8601NoT, HasExpressionLookup, TypeEngine[dt.time]): - """A type for ``datetime.time()`` objects.""" - - __visit_name__ = "time" - - def __init__(self, timezone: bool = False): - self.timezone = timezone - - def get_dbapi_type(self, dbapi): - return dbapi.DATETIME - - @property - def python_type(self): - return dt.time - - def _resolve_for_literal(self, value): - with_timezone = value.tzinfo is not None - if with_timezone and not self.timezone: - return TIME_TIMEZONE - else: - return self - - @util.memoized_property - def _expression_adaptations(self): - # Based on - # https://www.postgresql.org/docs/current/static/functions-datetime.html. - - return { - operators.add: {Date: DateTime, Interval: self.__class__}, - operators.sub: {Time: Interval, Interval: self.__class__}, - } - - def literal_processor(self, dialect): - return self._literal_processor_time(dialect) - - -class _Binary(TypeEngine[bytes]): - """Define base behavior for binary types.""" - - def __init__(self, length: Optional[int] = None): - self.length = length - - def literal_processor(self, dialect): - def process(value): - # TODO: this is useless for real world scenarios; implement - # real binary literals - value = value.decode( - dialect._legacy_binary_type_literal_encoding - ).replace("'", "''") - return "'%s'" % value - - return process - - @property - def python_type(self): - return bytes - - # Python 3 - sqlite3 doesn't need the `Binary` conversion - # here, though pg8000 does to indicate "bytea" - def bind_processor(self, dialect): - if dialect.dbapi is None: - return None - - DBAPIBinary = dialect.dbapi.Binary - - def process(value): - if value is not None: - return DBAPIBinary(value) - else: - return None - - return process - - # Python 3 has native bytes() type - # both sqlite3 and pg8000 seem to return it, - # psycopg2 as of 2.5 returns 'memoryview' - def result_processor(self, dialect, coltype): - if dialect.returns_native_bytes: - return None - - def process(value): - if value is not None: - value = bytes(value) - return value - - return process - - def coerce_compared_value(self, op, value): - """See :meth:`.TypeEngine.coerce_compared_value` for a description.""" - - if isinstance(value, str): - return self - else: - return super().coerce_compared_value(op, value) - - def get_dbapi_type(self, dbapi): - return dbapi.BINARY - - -class LargeBinary(_Binary): - """A type for large binary byte data. - - The :class:`.LargeBinary` type corresponds to a large and/or unlengthed - binary type for the target platform, such as BLOB on MySQL and BYTEA for - PostgreSQL. It also handles the necessary conversions for the DBAPI. - - """ - - __visit_name__ = "large_binary" - - def __init__(self, length: Optional[int] = None): - """ - Construct a LargeBinary type. - - :param length: optional, a length for the column for use in - DDL statements, for those binary types that accept a length, - such as the MySQL BLOB type. - - """ - _Binary.__init__(self, length=length) - - -class SchemaType(SchemaEventTarget, TypeEngineMixin): - """Add capabilities to a type which allow for schema-level DDL to be - associated with a type. - - Supports types that must be explicitly created/dropped (i.e. PG ENUM type) - as well as types that are complimented by table or schema level - constraints, triggers, and other rules. - - :class:`.SchemaType` classes can also be targets for the - :meth:`.DDLEvents.before_parent_attach` and - :meth:`.DDLEvents.after_parent_attach` events, where the events fire off - surrounding the association of the type object with a parent - :class:`_schema.Column`. - - .. seealso:: - - :class:`.Enum` - - :class:`.Boolean` - - - """ - - _use_schema_map = True - - name: Optional[str] - - def __init__( - self, - name: Optional[str] = None, - schema: Optional[str] = None, - metadata: Optional[MetaData] = None, - inherit_schema: bool = False, - quote: Optional[bool] = None, - _create_events: bool = True, - _adapted_from: Optional[SchemaType] = None, - ): - if name is not None: - self.name = quoted_name(name, quote) - else: - self.name = None - self.schema = schema - self.metadata = metadata - self.inherit_schema = inherit_schema - self._create_events = _create_events - - if _create_events and self.metadata: - event.listen( - self.metadata, - "before_create", - util.portable_instancemethod(self._on_metadata_create), - ) - event.listen( - self.metadata, - "after_drop", - util.portable_instancemethod(self._on_metadata_drop), - ) - - if _adapted_from: - self.dispatch = self.dispatch._join(_adapted_from.dispatch) - - def _set_parent(self, column, **kw): - # set parent hook is when this type is associated with a column. - # Column calls it for all SchemaEventTarget instances, either the - # base type and/or variants in _variant_mapping. - - # we want to register a second hook to trigger when that column is - # associated with a table. in that event, we and all of our variants - # may want to set up some state on the table such as a CheckConstraint - # that will conditionally render at DDL render time. - - # the base SchemaType also sets up events for - # on_table/metadata_create/drop in this method, which is used by - # "native" types with a separate CREATE/DROP e.g. Postgresql.ENUM - - column._on_table_attach(util.portable_instancemethod(self._set_table)) - - def _variant_mapping_for_set_table(self, column): - if column.type._variant_mapping: - variant_mapping = dict(column.type._variant_mapping) - variant_mapping["_default"] = column.type - else: - variant_mapping = None - return variant_mapping - - def _set_table(self, column, table): - if self.inherit_schema: - self.schema = table.schema - elif self.metadata and self.schema is None and self.metadata.schema: - self.schema = self.metadata.schema - - if not self._create_events: - return - - variant_mapping = self._variant_mapping_for_set_table(column) - - event.listen( - table, - "before_create", - util.portable_instancemethod( - self._on_table_create, {"variant_mapping": variant_mapping} - ), - ) - event.listen( - table, - "after_drop", - util.portable_instancemethod( - self._on_table_drop, {"variant_mapping": variant_mapping} - ), - ) - if self.metadata is None: - # if SchemaType were created w/ a metadata argument, these - # events would already have been associated with that metadata - # and would preclude an association with table.metadata - event.listen( - table.metadata, - "before_create", - util.portable_instancemethod( - self._on_metadata_create, - {"variant_mapping": variant_mapping}, - ), - ) - event.listen( - table.metadata, - "after_drop", - util.portable_instancemethod( - self._on_metadata_drop, - {"variant_mapping": variant_mapping}, - ), - ) - - def copy(self, **kw): - return self.adapt( - cast("Type[TypeEngine[Any]]", self.__class__), - _create_events=True, - ) - - @overload - def adapt(self, cls: Type[_TE], **kw: Any) -> _TE: ... - - @overload - def adapt( - self, cls: Type[TypeEngineMixin], **kw: Any - ) -> TypeEngine[Any]: ... - - def adapt( - self, cls: Type[Union[TypeEngine[Any], TypeEngineMixin]], **kw: Any - ) -> TypeEngine[Any]: - kw.setdefault("_create_events", False) - kw.setdefault("_adapted_from", self) - return super().adapt(cls, **kw) - - def create(self, bind, checkfirst=False): - """Issue CREATE DDL for this type, if applicable.""" - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t.create(bind, checkfirst=checkfirst) - - def drop(self, bind, checkfirst=False): - """Issue DROP DDL for this type, if applicable.""" - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t.drop(bind, checkfirst=checkfirst) - - def _on_table_create(self, target, bind, **kw): - if not self._is_impl_for_variant(bind.dialect, kw): - return - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t._on_table_create(target, bind, **kw) - - def _on_table_drop(self, target, bind, **kw): - if not self._is_impl_for_variant(bind.dialect, kw): - return - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t._on_table_drop(target, bind, **kw) - - def _on_metadata_create(self, target, bind, **kw): - if not self._is_impl_for_variant(bind.dialect, kw): - return - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t._on_metadata_create(target, bind, **kw) - - def _on_metadata_drop(self, target, bind, **kw): - if not self._is_impl_for_variant(bind.dialect, kw): - return - - t = self.dialect_impl(bind.dialect) - if isinstance(t, SchemaType) and t.__class__ is not self.__class__: - t._on_metadata_drop(target, bind, **kw) - - def _is_impl_for_variant(self, dialect, kw): - variant_mapping = kw.pop("variant_mapping", None) - - if not variant_mapping: - return True - - # for types that have _variant_mapping, all the impls in the map - # that are SchemaEventTarget subclasses get set up as event holders. - # this is so that constructs that need - # to be associated with the Table at dialect-agnostic time etc. like - # CheckConstraints can be set up with that table. they then add - # to these constraints a DDL check_rule that among other things - # will check this _is_impl_for_variant() method to determine when - # the dialect is known that we are part of the table's DDL sequence. - - # since PostgreSQL is the only DB that has ARRAY this can only - # be integration tested by PG-specific tests - def _we_are_the_impl(typ): - return ( - typ is self - or isinstance(typ, ARRAY) - and typ.item_type is self # type: ignore[comparison-overlap] - ) - - if dialect.name in variant_mapping and _we_are_the_impl( - variant_mapping[dialect.name] - ): - return True - elif dialect.name not in variant_mapping: - return _we_are_the_impl(variant_mapping["_default"]) - - -class Enum(String, SchemaType, Emulated, TypeEngine[Union[str, enum.Enum]]): - """Generic Enum Type. - - The :class:`.Enum` type provides a set of possible string values - which the column is constrained towards. - - The :class:`.Enum` type will make use of the backend's native "ENUM" - type if one is available; otherwise, it uses a VARCHAR datatype. - An option also exists to automatically produce a CHECK constraint - when the VARCHAR (so called "non-native") variant is produced; - see the :paramref:`.Enum.create_constraint` flag. - - The :class:`.Enum` type also provides in-Python validation of string - values during both read and write operations. When reading a value - from the database in a result set, the string value is always checked - against the list of possible values and a ``LookupError`` is raised - if no match is found. When passing a value to the database as a - plain string within a SQL statement, if the - :paramref:`.Enum.validate_strings` parameter is - set to True, a ``LookupError`` is raised for any string value that's - not located in the given list of possible values; note that this - impacts usage of LIKE expressions with enumerated values (an unusual - use case). - - The source of enumerated values may be a list of string values, or - alternatively a PEP-435-compliant enumerated class. For the purposes - of the :class:`.Enum` datatype, this class need only provide a - ``__members__`` method. - - When using an enumerated class, the enumerated objects are used - both for input and output, rather than strings as is the case with - a plain-string enumerated type:: - - import enum - from sqlalchemy import Enum - - class MyEnum(enum.Enum): - one = 1 - two = 2 - three = 3 - - t = Table( - 'data', MetaData(), - Column('value', Enum(MyEnum)) - ) - - connection.execute(t.insert(), {"value": MyEnum.two}) - assert connection.scalar(t.select()) is MyEnum.two - - Above, the string names of each element, e.g. "one", "two", "three", - are persisted to the database; the values of the Python Enum, here - indicated as integers, are **not** used; the value of each enum can - therefore be any kind of Python object whether or not it is persistable. - - In order to persist the values and not the names, the - :paramref:`.Enum.values_callable` parameter may be used. The value of - this parameter is a user-supplied callable, which is intended to be used - with a PEP-435-compliant enumerated class and returns a list of string - values to be persisted. For a simple enumeration that uses string values, - a callable such as ``lambda x: [e.value for e in x]`` is sufficient. - - .. seealso:: - - :ref:`orm_declarative_mapped_column_enums` - background on using - the :class:`_sqltypes.Enum` datatype with the ORM's - :ref:`ORM Annotated Declarative <orm_declarative_mapped_column>` - feature. - - :class:`_postgresql.ENUM` - PostgreSQL-specific type, - which has additional functionality. - - :class:`.mysql.ENUM` - MySQL-specific type - - """ - - __visit_name__ = "enum" - - def __init__(self, *enums: object, **kw: Any): - r"""Construct an enum. - - Keyword arguments which don't apply to a specific backend are ignored - by that backend. - - :param \*enums: either exactly one PEP-435 compliant enumerated type - or one or more string labels. - - :param create_constraint: defaults to False. When creating a - non-native enumerated type, also build a CHECK constraint on the - database against the valid values. - - .. note:: it is strongly recommended that the CHECK constraint - have an explicit name in order to support schema-management - concerns. This can be established either by setting the - :paramref:`.Enum.name` parameter or by setting up an - appropriate naming convention; see - :ref:`constraint_naming_conventions` for background. - - .. versionchanged:: 1.4 - this flag now defaults to False, meaning - no CHECK constraint is generated for a non-native enumerated - type. - - :param metadata: Associate this type directly with a ``MetaData`` - object. For types that exist on the target database as an - independent schema construct (PostgreSQL), this type will be - created and dropped within ``create_all()`` and ``drop_all()`` - operations. If the type is not associated with any ``MetaData`` - object, it will associate itself with each ``Table`` in which it is - used, and will be created when any of those individual tables are - created, after a check is performed for its existence. The type is - only dropped when ``drop_all()`` is called for that ``Table`` - object's metadata, however. - - The value of the :paramref:`_schema.MetaData.schema` parameter of - the :class:`_schema.MetaData` object, if set, will be used as the - default value of the :paramref:`_types.Enum.schema` on this object - if an explicit value is not otherwise supplied. - - .. versionchanged:: 1.4.12 :class:`_types.Enum` inherits the - :paramref:`_schema.MetaData.schema` parameter of the - :class:`_schema.MetaData` object if present, when passed using - the :paramref:`_types.Enum.metadata` parameter. - - :param name: The name of this type. This is required for PostgreSQL - and any future supported database which requires an explicitly - named type, or an explicitly named constraint in order to generate - the type and/or a table that uses it. If a PEP-435 enumerated - class was used, its name (converted to lower case) is used by - default. - - :param native_enum: Use the database's native ENUM type when - available. Defaults to True. When False, uses VARCHAR + check - constraint for all backends. When False, the VARCHAR length can be - controlled with :paramref:`.Enum.length`; currently "length" is - ignored if native_enum=True. - - :param length: Allows specifying a custom length for the VARCHAR - when a non-native enumeration datatype is used. By default it uses - the length of the longest value. - - .. versionchanged:: 2.0.0 The :paramref:`.Enum.length` parameter - is used unconditionally for ``VARCHAR`` rendering regardless of - the :paramref:`.Enum.native_enum` parameter, for those backends - where ``VARCHAR`` is used for enumerated datatypes. - - - :param schema: Schema name of this type. For types that exist on the - target database as an independent schema construct (PostgreSQL), - this parameter specifies the named schema in which the type is - present. - - If not present, the schema name will be taken from the - :class:`_schema.MetaData` collection if passed as - :paramref:`_types.Enum.metadata`, for a :class:`_schema.MetaData` - that includes the :paramref:`_schema.MetaData.schema` parameter. - - .. versionchanged:: 1.4.12 :class:`_types.Enum` inherits the - :paramref:`_schema.MetaData.schema` parameter of the - :class:`_schema.MetaData` object if present, when passed using - the :paramref:`_types.Enum.metadata` parameter. - - Otherwise, if the :paramref:`_types.Enum.inherit_schema` flag is set - to ``True``, the schema will be inherited from the associated - :class:`_schema.Table` object if any; when - :paramref:`_types.Enum.inherit_schema` is at its default of - ``False``, the owning table's schema is **not** used. - - - :param quote: Set explicit quoting preferences for the type's name. - - :param inherit_schema: When ``True``, the "schema" from the owning - :class:`_schema.Table` - will be copied to the "schema" attribute of this - :class:`.Enum`, replacing whatever value was passed for the - ``schema`` attribute. This also takes effect when using the - :meth:`_schema.Table.to_metadata` operation. - - :param validate_strings: when True, string values that are being - passed to the database in a SQL statement will be checked - for validity against the list of enumerated values. Unrecognized - values will result in a ``LookupError`` being raised. - - :param values_callable: A callable which will be passed the PEP-435 - compliant enumerated type, which should then return a list of string - values to be persisted. This allows for alternate usages such as - using the string value of an enum to be persisted to the database - instead of its name. The callable must return the values to be - persisted in the same order as iterating through the Enum's - ``__member__`` attribute. For example - ``lambda x: [i.value for i in x]``. - - .. versionadded:: 1.2.3 - - :param sort_key_function: a Python callable which may be used as the - "key" argument in the Python ``sorted()`` built-in. The SQLAlchemy - ORM requires that primary key columns which are mapped must - be sortable in some way. When using an unsortable enumeration - object such as a Python 3 ``Enum`` object, this parameter may be - used to set a default sort key function for the objects. By - default, the database value of the enumeration is used as the - sorting function. - - .. versionadded:: 1.3.8 - - :param omit_aliases: A boolean that when true will remove aliases from - pep 435 enums. defaults to ``True``. - - .. versionchanged:: 2.0 This parameter now defaults to True. - - """ - self._enum_init(enums, kw) - - @property - def _enums_argument(self): - if self.enum_class is not None: - return [self.enum_class] - else: - return self.enums - - def _enum_init(self, enums, kw): - """internal init for :class:`.Enum` and subclasses. - - friendly init helper used by subclasses to remove - all the Enum-specific keyword arguments from kw. Allows all - other arguments in kw to pass through. - - """ - self.native_enum = kw.pop("native_enum", True) - self.create_constraint = kw.pop("create_constraint", False) - self.values_callable = kw.pop("values_callable", None) - self._sort_key_function = kw.pop("sort_key_function", NO_ARG) - length_arg = kw.pop("length", NO_ARG) - self._omit_aliases = kw.pop("omit_aliases", True) - _disable_warnings = kw.pop("_disable_warnings", False) - values, objects = self._parse_into_values(enums, kw) - self._setup_for_values(values, objects, kw) - - self.validate_strings = kw.pop("validate_strings", False) - - if self.enums: - self._default_length = length = max(len(x) for x in self.enums) - else: - self._default_length = length = 0 - - if length_arg is not NO_ARG: - if ( - not _disable_warnings - and length_arg is not None - and length_arg < length - ): - raise ValueError( - "When provided, length must be larger or equal" - " than the length of the longest enum value. %s < %s" - % (length_arg, length) - ) - length = length_arg - - self._valid_lookup[None] = self._object_lookup[None] = None - - super().__init__(length=length) - - # assign name to the given enum class if no other name, and this - # enum is not an "empty" enum. if the enum is "empty" we assume - # this is a template enum that will be used to generate - # new Enum classes. - if self.enum_class and values: - kw.setdefault("name", self.enum_class.__name__.lower()) - SchemaType.__init__( - self, - name=kw.pop("name", None), - schema=kw.pop("schema", None), - metadata=kw.pop("metadata", None), - inherit_schema=kw.pop("inherit_schema", False), - quote=kw.pop("quote", None), - _create_events=kw.pop("_create_events", True), - _adapted_from=kw.pop("_adapted_from", None), - ) - - def _parse_into_values(self, enums, kw): - if not enums and "_enums" in kw: - enums = kw.pop("_enums") - - if len(enums) == 1 and hasattr(enums[0], "__members__"): - self.enum_class = enums[0] - - _members = self.enum_class.__members__ - - if self._omit_aliases is True: - # remove aliases - members = OrderedDict( - (n, v) for n, v in _members.items() if v.name == n - ) - else: - members = _members - if self.values_callable: - values = self.values_callable(self.enum_class) - else: - values = list(members) - objects = [members[k] for k in members] - return values, objects - else: - self.enum_class = None - return enums, enums - - def _resolve_for_literal(self, value: Any) -> Enum: - tv = type(value) - typ = self._resolve_for_python_type(tv, tv, tv) - assert typ is not None - return typ - - def _resolve_for_python_type( - self, - python_type: Type[Any], - matched_on: _MatchedOnType, - matched_on_flattened: Type[Any], - ) -> Optional[Enum]: - # "generic form" indicates we were placed in a type map - # as ``sqlalchemy.Enum(enum.Enum)`` which indicates we need to - # get enumerated values from the datatype - we_are_generic_form = self._enums_argument == [enum.Enum] - - native_enum = None - - if not we_are_generic_form and python_type is matched_on: - # if we have enumerated values, and the incoming python - # type is exactly the one that matched in the type map, - # then we use these enumerated values and dont try to parse - # what's incoming - enum_args = self._enums_argument - - elif is_literal(python_type): - # for a literal, where we need to get its contents, parse it out. - enum_args = typing_get_args(python_type) - bad_args = [arg for arg in enum_args if not isinstance(arg, str)] - if bad_args: - raise exc.ArgumentError( - f"Can't create string-based Enum datatype from non-string " - f"values: {', '.join(repr(x) for x in bad_args)}. Please " - f"provide an explicit Enum datatype for this Python type" - ) - native_enum = False - elif isinstance(python_type, type) and issubclass( - python_type, enum.Enum - ): - # same for an enum.Enum - enum_args = [python_type] - - else: - enum_args = self._enums_argument - - # make a new Enum that looks like this one. - # arguments or other rules - kw = self._make_enum_kw({}) - - if native_enum is False: - kw["native_enum"] = False - - kw["length"] = NO_ARG if self.length == 0 else self.length - return cast( - Enum, - self._generic_type_affinity(_enums=enum_args, **kw), # type: ignore # noqa: E501 - ) - - def _setup_for_values(self, values, objects, kw): - self.enums = list(values) - - self._valid_lookup = dict(zip(reversed(objects), reversed(values))) - - self._object_lookup = dict(zip(values, objects)) - - self._valid_lookup.update( - [ - (value, self._valid_lookup[self._object_lookup[value]]) - for value in values - ] - ) - - @property - def sort_key_function(self): - if self._sort_key_function is NO_ARG: - return self._db_value_for_elem - else: - return self._sort_key_function - - @property - def native(self): - return self.native_enum - - def _db_value_for_elem(self, elem): - try: - return self._valid_lookup[elem] - except KeyError as err: - # for unknown string values, we return as is. While we can - # validate these if we wanted, that does not allow for lesser-used - # end-user use cases, such as using a LIKE comparison with an enum, - # or for an application that wishes to apply string tests to an - # ENUM (see [ticket:3725]). While we can decide to differentiate - # here between an INSERT statement and a criteria used in a SELECT, - # for now we're staying conservative w/ behavioral changes (perhaps - # someone has a trigger that handles strings on INSERT) - if not self.validate_strings and isinstance(elem, str): - return elem - else: - raise LookupError( - "'%s' is not among the defined enum values. " - "Enum name: %s. Possible values: %s" - % ( - elem, - self.name, - langhelpers.repr_tuple_names(self.enums), - ) - ) from err - - class Comparator(String.Comparator[str]): - __slots__ = () - - type: String - - def _adapt_expression( - self, - op: OperatorType, - other_comparator: TypeEngine.Comparator[Any], - ) -> Tuple[OperatorType, TypeEngine[Any]]: - op, typ = super()._adapt_expression(op, other_comparator) - if op is operators.concat_op: - typ = String(self.type.length) - return op, typ - - comparator_factory = Comparator - - def _object_value_for_elem(self, elem): - try: - return self._object_lookup[elem] - except KeyError as err: - raise LookupError( - "'%s' is not among the defined enum values. " - "Enum name: %s. Possible values: %s" - % ( - elem, - self.name, - langhelpers.repr_tuple_names(self.enums), - ) - ) from err - - def __repr__(self): - return util.generic_repr( - self, - additional_kw=[ - ("native_enum", True), - ("create_constraint", False), - ("length", self._default_length), - ], - to_inspect=[Enum, SchemaType], - ) - - def as_generic(self, allow_nulltype=False): - try: - args = self.enums - except AttributeError: - raise NotImplementedError( - "TypeEngine.as_generic() heuristic " - "is undefined for types that inherit Enum but do not have " - "an `enums` attribute." - ) from None - - return util.constructor_copy( - self, self._generic_type_affinity, *args, _disable_warnings=True - ) - - def _make_enum_kw(self, kw): - kw.setdefault("validate_strings", self.validate_strings) - if self.name: - kw.setdefault("name", self.name) - kw.setdefault("schema", self.schema) - kw.setdefault("inherit_schema", self.inherit_schema) - kw.setdefault("metadata", self.metadata) - kw.setdefault("native_enum", self.native_enum) - kw.setdefault("values_callable", self.values_callable) - kw.setdefault("create_constraint", self.create_constraint) - kw.setdefault("length", self.length) - kw.setdefault("omit_aliases", self._omit_aliases) - return kw - - def adapt_to_emulated(self, impltype, **kw): - self._make_enum_kw(kw) - kw["_disable_warnings"] = True - kw.setdefault("_create_events", False) - assert "_enums" in kw - return impltype(**kw) - - def adapt(self, impltype, **kw): - kw["_enums"] = self._enums_argument - kw["_disable_warnings"] = True - return super().adapt(impltype, **kw) - - def _should_create_constraint(self, compiler, **kw): - if not self._is_impl_for_variant(compiler.dialect, kw): - return False - return ( - not self.native_enum or not compiler.dialect.supports_native_enum - ) - - @util.preload_module("sqlalchemy.sql.schema") - def _set_table(self, column, table): - schema = util.preloaded.sql_schema - SchemaType._set_table(self, column, table) - - if not self.create_constraint: - return - - variant_mapping = self._variant_mapping_for_set_table(column) - - e = schema.CheckConstraint( - type_coerce(column, String()).in_(self.enums), - name=_NONE_NAME if self.name is None else self.name, - _create_rule=util.portable_instancemethod( - self._should_create_constraint, - {"variant_mapping": variant_mapping}, - ), - _type_bound=True, - ) - assert e.table is table - - def literal_processor(self, dialect): - parent_processor = super().literal_processor(dialect) - - def process(value): - value = self._db_value_for_elem(value) - if parent_processor: - value = parent_processor(value) - return value - - return process - - def bind_processor(self, dialect): - parent_processor = super().bind_processor(dialect) - - def process(value): - value = self._db_value_for_elem(value) - if parent_processor: - value = parent_processor(value) - return value - - return process - - def result_processor(self, dialect, coltype): - parent_processor = super().result_processor(dialect, coltype) - - def process(value): - if parent_processor: - value = parent_processor(value) - - value = self._object_value_for_elem(value) - return value - - return process - - def copy(self, **kw): - return SchemaType.copy(self, **kw) - - @property - def python_type(self): - if self.enum_class: - return self.enum_class - else: - return super().python_type - - -class PickleType(TypeDecorator[object]): - """Holds Python objects, which are serialized using pickle. - - PickleType builds upon the Binary type to apply Python's - ``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on - the way out, allowing any pickleable Python object to be stored as - a serialized binary field. - - To allow ORM change events to propagate for elements associated - with :class:`.PickleType`, see :ref:`mutable_toplevel`. - - """ - - impl = LargeBinary - cache_ok = True - - def __init__( - self, - protocol: int = pickle.HIGHEST_PROTOCOL, - pickler: Any = None, - comparator: Optional[Callable[[Any, Any], bool]] = None, - impl: Optional[_TypeEngineArgument[Any]] = None, - ): - """ - Construct a PickleType. - - :param protocol: defaults to ``pickle.HIGHEST_PROTOCOL``. - - :param pickler: defaults to pickle. May be any object with - pickle-compatible ``dumps`` and ``loads`` methods. - - :param comparator: a 2-arg callable predicate used - to compare values of this type. If left as ``None``, - the Python "equals" operator is used to compare values. - - :param impl: A binary-storing :class:`_types.TypeEngine` class or - instance to use in place of the default :class:`_types.LargeBinary`. - For example the :class: `_mysql.LONGBLOB` class may be more effective - when using MySQL. - - .. versionadded:: 1.4.20 - - """ - self.protocol = protocol - self.pickler = pickler or pickle - self.comparator = comparator - super().__init__() - - if impl: - # custom impl is not necessarily a LargeBinary subclass. - # make an exception to typing for this - self.impl = to_instance(impl) # type: ignore - - def __reduce__(self): - return PickleType, (self.protocol, None, self.comparator) - - def bind_processor(self, dialect): - impl_processor = self.impl_instance.bind_processor(dialect) - dumps = self.pickler.dumps - protocol = self.protocol - if impl_processor: - fixed_impl_processor = impl_processor - - def process(value): - if value is not None: - value = dumps(value, protocol) - return fixed_impl_processor(value) - - else: - - def process(value): - if value is not None: - value = dumps(value, protocol) - return value - - return process - - def result_processor(self, dialect, coltype): - impl_processor = self.impl_instance.result_processor(dialect, coltype) - loads = self.pickler.loads - if impl_processor: - fixed_impl_processor = impl_processor - - def process(value): - value = fixed_impl_processor(value) - if value is None: - return None - return loads(value) - - else: - - def process(value): - if value is None: - return None - return loads(value) - - return process - - def compare_values(self, x, y): - if self.comparator: - return self.comparator(x, y) - else: - return x == y - - -class Boolean(SchemaType, Emulated, TypeEngine[bool]): - """A bool datatype. - - :class:`.Boolean` typically uses BOOLEAN or SMALLINT on the DDL side, - and on the Python side deals in ``True`` or ``False``. - - The :class:`.Boolean` datatype currently has two levels of assertion - that the values persisted are simple true/false values. For all - backends, only the Python values ``None``, ``True``, ``False``, ``1`` - or ``0`` are accepted as parameter values. For those backends that - don't support a "native boolean" datatype, an option exists to - also create a CHECK constraint on the target column - - .. versionchanged:: 1.2 the :class:`.Boolean` datatype now asserts that - incoming Python values are already in pure boolean form. - - - """ - - __visit_name__ = "boolean" - native = True - - def __init__( - self, - create_constraint: bool = False, - name: Optional[str] = None, - _create_events: bool = True, - _adapted_from: Optional[SchemaType] = None, - ): - """Construct a Boolean. - - :param create_constraint: defaults to False. If the boolean - is generated as an int/smallint, also create a CHECK constraint - on the table that ensures 1 or 0 as a value. - - .. note:: it is strongly recommended that the CHECK constraint - have an explicit name in order to support schema-management - concerns. This can be established either by setting the - :paramref:`.Boolean.name` parameter or by setting up an - appropriate naming convention; see - :ref:`constraint_naming_conventions` for background. - - .. versionchanged:: 1.4 - this flag now defaults to False, meaning - no CHECK constraint is generated for a non-native enumerated - type. - - :param name: if a CHECK constraint is generated, specify - the name of the constraint. - - """ - self.create_constraint = create_constraint - self.name = name - self._create_events = _create_events - if _adapted_from: - self.dispatch = self.dispatch._join(_adapted_from.dispatch) - - def _should_create_constraint(self, compiler, **kw): - if not self._is_impl_for_variant(compiler.dialect, kw): - return False - return ( - not compiler.dialect.supports_native_boolean - and compiler.dialect.non_native_boolean_check_constraint - ) - - @util.preload_module("sqlalchemy.sql.schema") - def _set_table(self, column, table): - schema = util.preloaded.sql_schema - if not self.create_constraint: - return - - variant_mapping = self._variant_mapping_for_set_table(column) - - e = schema.CheckConstraint( - type_coerce(column, self).in_([0, 1]), - name=_NONE_NAME if self.name is None else self.name, - _create_rule=util.portable_instancemethod( - self._should_create_constraint, - {"variant_mapping": variant_mapping}, - ), - _type_bound=True, - ) - assert e.table is table - - @property - def python_type(self): - return bool - - _strict_bools = frozenset([None, True, False]) - - def _strict_as_bool(self, value): - if value not in self._strict_bools: - if not isinstance(value, int): - raise TypeError("Not a boolean value: %r" % (value,)) - else: - raise ValueError( - "Value %r is not None, True, or False" % (value,) - ) - return value - - def literal_processor(self, dialect): - compiler = dialect.statement_compiler(dialect, None) - true = compiler.visit_true(None) - false = compiler.visit_false(None) - - def process(value): - return true if self._strict_as_bool(value) else false - - return process - - def bind_processor(self, dialect): - _strict_as_bool = self._strict_as_bool - - _coerce: Union[Type[bool], Type[int]] - - if dialect.supports_native_boolean: - _coerce = bool - else: - _coerce = int - - def process(value): - value = _strict_as_bool(value) - if value is not None: - value = _coerce(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if dialect.supports_native_boolean: - return None - else: - return processors.int_to_boolean - - -class _AbstractInterval(HasExpressionLookup, TypeEngine[dt.timedelta]): - @util.memoized_property - def _expression_adaptations(self): - # Based on - # https://www.postgresql.org/docs/current/static/functions-datetime.html. - - return { - operators.add: { - Date: DateTime, - Interval: self.__class__, - DateTime: DateTime, - Time: Time, - }, - operators.sub: {Interval: self.__class__}, - operators.mul: {Numeric: self.__class__}, - operators.truediv: {Numeric: self.__class__}, - } - - @util.ro_non_memoized_property - def _type_affinity(self) -> Type[Interval]: - return Interval - - -class Interval(Emulated, _AbstractInterval, TypeDecorator[dt.timedelta]): - """A type for ``datetime.timedelta()`` objects. - - The Interval type deals with ``datetime.timedelta`` objects. In - PostgreSQL and Oracle, the native ``INTERVAL`` type is used; for others, - the value is stored as a date which is relative to the "epoch" - (Jan. 1, 1970). - - Note that the ``Interval`` type does not currently provide date arithmetic - operations on platforms which do not support interval types natively. Such - operations usually require transformation of both sides of the expression - (such as, conversion of both sides into integer epoch values first) which - currently is a manual procedure (such as via - :attr:`~sqlalchemy.sql.expression.func`). - - """ - - impl = DateTime - epoch = dt.datetime.fromtimestamp(0, dt.timezone.utc).replace(tzinfo=None) - cache_ok = True - - def __init__( - self, - native: bool = True, - second_precision: Optional[int] = None, - day_precision: Optional[int] = None, - ): - """Construct an Interval object. - - :param native: when True, use the actual - INTERVAL type provided by the database, if - supported (currently PostgreSQL, Oracle). - Otherwise, represent the interval data as - an epoch value regardless. - - :param second_precision: For native interval types - which support a "fractional seconds precision" parameter, - i.e. Oracle and PostgreSQL - - :param day_precision: for native interval types which - support a "day precision" parameter, i.e. Oracle. - - """ - super().__init__() - self.native = native - self.second_precision = second_precision - self.day_precision = day_precision - - class Comparator( - TypeDecorator.Comparator[_CT], - _AbstractInterval.Comparator[_CT], - ): - __slots__ = () - - comparator_factory = Comparator - - @property - def python_type(self): - return dt.timedelta - - def adapt_to_emulated(self, impltype, **kw): - return _AbstractInterval.adapt(self, impltype, **kw) - - def coerce_compared_value(self, op, value): - return self.impl_instance.coerce_compared_value(op, value) - - def bind_processor( - self, dialect: Dialect - ) -> _BindProcessorType[dt.timedelta]: - if TYPE_CHECKING: - assert isinstance(self.impl_instance, DateTime) - impl_processor = self.impl_instance.bind_processor(dialect) - epoch = self.epoch - if impl_processor: - fixed_impl_processor = impl_processor - - def process( - value: Optional[dt.timedelta], - ) -> Any: - if value is not None: - dt_value = epoch + value - else: - dt_value = None - return fixed_impl_processor(dt_value) - - else: - - def process( - value: Optional[dt.timedelta], - ) -> Any: - if value is not None: - dt_value = epoch + value - else: - dt_value = None - return dt_value - - return process - - def result_processor( - self, dialect: Dialect, coltype: Any - ) -> _ResultProcessorType[dt.timedelta]: - if TYPE_CHECKING: - assert isinstance(self.impl_instance, DateTime) - impl_processor = self.impl_instance.result_processor(dialect, coltype) - epoch = self.epoch - if impl_processor: - fixed_impl_processor = impl_processor - - def process(value: Any) -> Optional[dt.timedelta]: - dt_value = fixed_impl_processor(value) - if dt_value is None: - return None - return dt_value - epoch - - else: - - def process(value: Any) -> Optional[dt.timedelta]: - if value is None: - return None - return value - epoch # type: ignore - - return process - - -class JSON(Indexable, TypeEngine[Any]): - """Represent a SQL JSON type. - - .. note:: :class:`_types.JSON` - is provided as a facade for vendor-specific - JSON types. Since it supports JSON SQL operations, it only - works on backends that have an actual JSON type, currently: - - * PostgreSQL - see :class:`sqlalchemy.dialects.postgresql.JSON` and - :class:`sqlalchemy.dialects.postgresql.JSONB` for backend-specific - notes - - * MySQL - see - :class:`sqlalchemy.dialects.mysql.JSON` for backend-specific notes - - * SQLite as of version 3.9 - see - :class:`sqlalchemy.dialects.sqlite.JSON` for backend-specific notes - - * Microsoft SQL Server 2016 and later - see - :class:`sqlalchemy.dialects.mssql.JSON` for backend-specific notes - - :class:`_types.JSON` is part of the Core in support of the growing - popularity of native JSON datatypes. - - The :class:`_types.JSON` type stores arbitrary JSON format data, e.g.:: - - data_table = Table('data_table', metadata, - Column('id', Integer, primary_key=True), - Column('data', JSON) - ) - - with engine.connect() as conn: - conn.execute( - data_table.insert(), - {"data": {"key1": "value1", "key2": "value2"}} - ) - - **JSON-Specific Expression Operators** - - The :class:`_types.JSON` - datatype provides these additional SQL operations: - - * Keyed index operations:: - - data_table.c.data['some key'] - - * Integer index operations:: - - data_table.c.data[3] - - * Path index operations:: - - data_table.c.data[('key_1', 'key_2', 5, ..., 'key_n')] - - * Data casters for specific JSON element types, subsequent to an index - or path operation being invoked:: - - data_table.c.data["some key"].as_integer() - - .. versionadded:: 1.3.11 - - Additional operations may be available from the dialect-specific versions - of :class:`_types.JSON`, such as - :class:`sqlalchemy.dialects.postgresql.JSON` and - :class:`sqlalchemy.dialects.postgresql.JSONB` which both offer additional - PostgreSQL-specific operations. - - **Casting JSON Elements to Other Types** - - Index operations, i.e. those invoked by calling upon the expression using - the Python bracket operator as in ``some_column['some key']``, return an - expression object whose type defaults to :class:`_types.JSON` by default, - so that - further JSON-oriented instructions may be called upon the result type. - However, it is likely more common that an index operation is expected - to return a specific scalar element, such as a string or integer. In - order to provide access to these elements in a backend-agnostic way, - a series of data casters are provided: - - * :meth:`.JSON.Comparator.as_string` - return the element as a string - - * :meth:`.JSON.Comparator.as_boolean` - return the element as a boolean - - * :meth:`.JSON.Comparator.as_float` - return the element as a float - - * :meth:`.JSON.Comparator.as_integer` - return the element as an integer - - These data casters are implemented by supporting dialects in order to - assure that comparisons to the above types will work as expected, such as:: - - # integer comparison - data_table.c.data["some_integer_key"].as_integer() == 5 - - # boolean comparison - data_table.c.data["some_boolean"].as_boolean() == True - - .. versionadded:: 1.3.11 Added type-specific casters for the basic JSON - data element types. - - .. note:: - - The data caster functions are new in version 1.3.11, and supersede - the previous documented approaches of using CAST; for reference, - this looked like:: - - from sqlalchemy import cast, type_coerce - from sqlalchemy import String, JSON - cast( - data_table.c.data['some_key'], String - ) == type_coerce(55, JSON) - - The above case now works directly as:: - - data_table.c.data['some_key'].as_integer() == 5 - - For details on the previous comparison approach within the 1.3.x - series, see the documentation for SQLAlchemy 1.2 or the included HTML - files in the doc/ directory of the version's distribution. - - **Detecting Changes in JSON columns when using the ORM** - - The :class:`_types.JSON` type, when used with the SQLAlchemy ORM, does not - detect in-place mutations to the structure. In order to detect these, the - :mod:`sqlalchemy.ext.mutable` extension must be used, most typically - using the :class:`.MutableDict` class. This extension will - allow "in-place" changes to the datastructure to produce events which - will be detected by the unit of work. See the example at :class:`.HSTORE` - for a simple example involving a dictionary. - - Alternatively, assigning a JSON structure to an ORM element that - replaces the old one will always trigger a change event. - - **Support for JSON null vs. SQL NULL** - - When working with NULL values, the :class:`_types.JSON` type recommends the - use of two specific constants in order to differentiate between a column - that evaluates to SQL NULL, e.g. no value, vs. the JSON-encoded string of - ``"null"``. To insert or select against a value that is SQL NULL, use the - constant :func:`.null`. This symbol may be passed as a parameter value - specifically when using the :class:`_types.JSON` datatype, which contains - special logic that interprets this symbol to mean that the column value - should be SQL NULL as opposed to JSON ``"null"``:: - - from sqlalchemy import null - conn.execute(table.insert(), {"json_value": null()}) - - To insert or select against a value that is JSON ``"null"``, use the - constant :attr:`_types.JSON.NULL`:: - - conn.execute(table.insert(), {"json_value": JSON.NULL}) - - The :class:`_types.JSON` type supports a flag - :paramref:`_types.JSON.none_as_null` which when set to True will result - in the Python constant ``None`` evaluating to the value of SQL - NULL, and when set to False results in the Python constant - ``None`` evaluating to the value of JSON ``"null"``. The Python - value ``None`` may be used in conjunction with either - :attr:`_types.JSON.NULL` and :func:`.null` in order to indicate NULL - values, but care must be taken as to the value of the - :paramref:`_types.JSON.none_as_null` in these cases. - - **Customizing the JSON Serializer** - - The JSON serializer and deserializer used by :class:`_types.JSON` - defaults to - Python's ``json.dumps`` and ``json.loads`` functions; in the case of the - psycopg2 dialect, psycopg2 may be using its own custom loader function. - - In order to affect the serializer / deserializer, they are currently - configurable at the :func:`_sa.create_engine` level via the - :paramref:`_sa.create_engine.json_serializer` and - :paramref:`_sa.create_engine.json_deserializer` parameters. For example, - to turn off ``ensure_ascii``:: - - engine = create_engine( - "sqlite://", - json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False)) - - .. versionchanged:: 1.3.7 - - SQLite dialect's ``json_serializer`` and ``json_deserializer`` - parameters renamed from ``_json_serializer`` and - ``_json_deserializer``. - - .. seealso:: - - :class:`sqlalchemy.dialects.postgresql.JSON` - - :class:`sqlalchemy.dialects.postgresql.JSONB` - - :class:`sqlalchemy.dialects.mysql.JSON` - - :class:`sqlalchemy.dialects.sqlite.JSON` - - """ - - __visit_name__ = "JSON" - - hashable = False - NULL = util.symbol("JSON_NULL") - """Describe the json value of NULL. - - This value is used to force the JSON value of ``"null"`` to be - used as the value. A value of Python ``None`` will be recognized - either as SQL NULL or JSON ``"null"``, based on the setting - of the :paramref:`_types.JSON.none_as_null` flag; the - :attr:`_types.JSON.NULL` - constant can be used to always resolve to JSON ``"null"`` regardless - of this setting. This is in contrast to the :func:`_expression.null` - construct, - which always resolves to SQL NULL. E.g.:: - - from sqlalchemy import null - from sqlalchemy.dialects.postgresql import JSON - - # will *always* insert SQL NULL - obj1 = MyObject(json_value=null()) - - # will *always* insert JSON string "null" - obj2 = MyObject(json_value=JSON.NULL) - - session.add_all([obj1, obj2]) - session.commit() - - In order to set JSON NULL as a default value for a column, the most - transparent method is to use :func:`_expression.text`:: - - Table( - 'my_table', metadata, - Column('json_data', JSON, default=text("'null'")) - ) - - While it is possible to use :attr:`_types.JSON.NULL` in this context, the - :attr:`_types.JSON.NULL` value will be returned as the value of the - column, - which in the context of the ORM or other repurposing of the default - value, may not be desirable. Using a SQL expression means the value - will be re-fetched from the database within the context of retrieving - generated defaults. - - - """ - - def __init__(self, none_as_null: bool = False): - """Construct a :class:`_types.JSON` type. - - :param none_as_null=False: if True, persist the value ``None`` as a - SQL NULL value, not the JSON encoding of ``null``. Note that when this - flag is False, the :func:`.null` construct can still be used to - persist a NULL value, which may be passed directly as a parameter - value that is specially interpreted by the :class:`_types.JSON` type - as SQL NULL:: - - from sqlalchemy import null - conn.execute(table.insert(), {"data": null()}) - - .. note:: - - :paramref:`_types.JSON.none_as_null` does **not** apply to the - values passed to :paramref:`_schema.Column.default` and - :paramref:`_schema.Column.server_default`; a value of ``None`` - passed for these parameters means "no default present". - - Additionally, when used in SQL comparison expressions, the - Python value ``None`` continues to refer to SQL null, and not - JSON NULL. The :paramref:`_types.JSON.none_as_null` flag refers - explicitly to the **persistence** of the value within an - INSERT or UPDATE statement. The :attr:`_types.JSON.NULL` - value should be used for SQL expressions that wish to compare to - JSON null. - - .. seealso:: - - :attr:`.types.JSON.NULL` - - """ - self.none_as_null = none_as_null - - class JSONElementType(TypeEngine[Any]): - """Common function for index / path elements in a JSON expression.""" - - _integer = Integer() - _string = String() - - def string_bind_processor(self, dialect): - return self._string._cached_bind_processor(dialect) - - def string_literal_processor(self, dialect): - return self._string._cached_literal_processor(dialect) - - def bind_processor(self, dialect): - int_processor = self._integer._cached_bind_processor(dialect) - string_processor = self.string_bind_processor(dialect) - - def process(value): - if int_processor and isinstance(value, int): - value = int_processor(value) - elif string_processor and isinstance(value, str): - value = string_processor(value) - return value - - return process - - def literal_processor(self, dialect): - int_processor = self._integer._cached_literal_processor(dialect) - string_processor = self.string_literal_processor(dialect) - - def process(value): - if int_processor and isinstance(value, int): - value = int_processor(value) - elif string_processor and isinstance(value, str): - value = string_processor(value) - else: - raise NotImplementedError() - - return value - - return process - - class JSONIndexType(JSONElementType): - """Placeholder for the datatype of a JSON index value. - - This allows execution-time processing of JSON index values - for special syntaxes. - - """ - - class JSONIntIndexType(JSONIndexType): - """Placeholder for the datatype of a JSON index value. - - This allows execution-time processing of JSON index values - for special syntaxes. - - """ - - class JSONStrIndexType(JSONIndexType): - """Placeholder for the datatype of a JSON index value. - - This allows execution-time processing of JSON index values - for special syntaxes. - - """ - - class JSONPathType(JSONElementType): - """Placeholder type for JSON path operations. - - This allows execution-time processing of a path-based - index value into a specific SQL syntax. - - """ - - __visit_name__ = "json_path" - - class Comparator(Indexable.Comparator[_T], Concatenable.Comparator[_T]): - """Define comparison operations for :class:`_types.JSON`.""" - - __slots__ = () - - def _setup_getitem(self, index): - if not isinstance(index, str) and isinstance( - index, collections_abc.Sequence - ): - index = coercions.expect( - roles.BinaryElementRole, - index, - expr=self.expr, - operator=operators.json_path_getitem_op, - bindparam_type=JSON.JSONPathType, - ) - - operator = operators.json_path_getitem_op - else: - index = coercions.expect( - roles.BinaryElementRole, - index, - expr=self.expr, - operator=operators.json_getitem_op, - bindparam_type=( - JSON.JSONIntIndexType - if isinstance(index, int) - else JSON.JSONStrIndexType - ), - ) - operator = operators.json_getitem_op - - return operator, index, self.type - - def as_boolean(self): - """Cast an indexed value as boolean. - - e.g.:: - - stmt = select( - mytable.c.json_column['some_data'].as_boolean() - ).where( - mytable.c.json_column['some_data'].as_boolean() == True - ) - - .. versionadded:: 1.3.11 - - """ - return self._binary_w_type(Boolean(), "as_boolean") - - def as_string(self): - """Cast an indexed value as string. - - e.g.:: - - stmt = select( - mytable.c.json_column['some_data'].as_string() - ).where( - mytable.c.json_column['some_data'].as_string() == - 'some string' - ) - - .. versionadded:: 1.3.11 - - """ - return self._binary_w_type(Unicode(), "as_string") - - def as_integer(self): - """Cast an indexed value as integer. - - e.g.:: - - stmt = select( - mytable.c.json_column['some_data'].as_integer() - ).where( - mytable.c.json_column['some_data'].as_integer() == 5 - ) - - .. versionadded:: 1.3.11 - - """ - return self._binary_w_type(Integer(), "as_integer") - - def as_float(self): - """Cast an indexed value as float. - - e.g.:: - - stmt = select( - mytable.c.json_column['some_data'].as_float() - ).where( - mytable.c.json_column['some_data'].as_float() == 29.75 - ) - - .. versionadded:: 1.3.11 - - """ - return self._binary_w_type(Float(), "as_float") - - def as_numeric(self, precision, scale, asdecimal=True): - """Cast an indexed value as numeric/decimal. - - e.g.:: - - stmt = select( - mytable.c.json_column['some_data'].as_numeric(10, 6) - ).where( - mytable.c. - json_column['some_data'].as_numeric(10, 6) == 29.75 - ) - - .. versionadded:: 1.4.0b2 - - """ - return self._binary_w_type( - Numeric(precision, scale, asdecimal=asdecimal), "as_numeric" - ) - - def as_json(self): - """Cast an indexed value as JSON. - - e.g.:: - - stmt = select(mytable.c.json_column['some_data'].as_json()) - - This is typically the default behavior of indexed elements in any - case. - - Note that comparison of full JSON structures may not be - supported by all backends. - - .. versionadded:: 1.3.11 - - """ - return self.expr - - def _binary_w_type(self, typ, method_name): - if not isinstance( - self.expr, elements.BinaryExpression - ) or self.expr.operator not in ( - operators.json_getitem_op, - operators.json_path_getitem_op, - ): - raise exc.InvalidRequestError( - "The JSON cast operator JSON.%s() only works with a JSON " - "index expression e.g. col['q'].%s()" - % (method_name, method_name) - ) - expr = self.expr._clone() - expr.type = typ - return expr - - comparator_factory = Comparator - - @property - def python_type(self): - return dict - - @property # type: ignore # mypy property bug - def should_evaluate_none(self): - """Alias of :attr:`_types.JSON.none_as_null`""" - return not self.none_as_null - - @should_evaluate_none.setter - def should_evaluate_none(self, value): - self.none_as_null = not value - - @util.memoized_property - def _str_impl(self): - return String() - - def _make_bind_processor(self, string_process, json_serializer): - if string_process: - - def process(value): - if value is self.NULL: - value = None - elif isinstance(value, elements.Null) or ( - value is None and self.none_as_null - ): - return None - - serialized = json_serializer(value) - return string_process(serialized) - - else: - - def process(value): - if value is self.NULL: - value = None - elif isinstance(value, elements.Null) or ( - value is None and self.none_as_null - ): - return None - - return json_serializer(value) - - return process - - def bind_processor(self, dialect): - string_process = self._str_impl.bind_processor(dialect) - json_serializer = dialect._json_serializer or json.dumps - - return self._make_bind_processor(string_process, json_serializer) - - def result_processor(self, dialect, coltype): - string_process = self._str_impl.result_processor(dialect, coltype) - json_deserializer = dialect._json_deserializer or json.loads - - def process(value): - if value is None: - return None - if string_process: - value = string_process(value) - return json_deserializer(value) - - return process - - -class ARRAY( - SchemaEventTarget, Indexable, Concatenable, TypeEngine[Sequence[Any]] -): - """Represent a SQL Array type. - - .. note:: This type serves as the basis for all ARRAY operations. - However, currently **only the PostgreSQL backend has support for SQL - arrays in SQLAlchemy**. It is recommended to use the PostgreSQL-specific - :class:`sqlalchemy.dialects.postgresql.ARRAY` type directly when using - ARRAY types with PostgreSQL, as it provides additional operators - specific to that backend. - - :class:`_types.ARRAY` is part of the Core in support of various SQL - standard functions such as :class:`_functions.array_agg` - which explicitly involve - arrays; however, with the exception of the PostgreSQL backend and possibly - some third-party dialects, no other SQLAlchemy built-in dialect has support - for this type. - - An :class:`_types.ARRAY` type is constructed given the "type" - of element:: - - mytable = Table("mytable", metadata, - Column("data", ARRAY(Integer)) - ) - - The above type represents an N-dimensional array, - meaning a supporting backend such as PostgreSQL will interpret values - with any number of dimensions automatically. To produce an INSERT - construct that passes in a 1-dimensional array of integers:: - - connection.execute( - mytable.insert(), - {"data": [1,2,3]} - ) - - The :class:`_types.ARRAY` type can be constructed given a fixed number - of dimensions:: - - mytable = Table("mytable", metadata, - Column("data", ARRAY(Integer, dimensions=2)) - ) - - Sending a number of dimensions is optional, but recommended if the - datatype is to represent arrays of more than one dimension. This number - is used: - - * When emitting the type declaration itself to the database, e.g. - ``INTEGER[][]`` - - * When translating Python values to database values, and vice versa, e.g. - an ARRAY of :class:`.Unicode` objects uses this number to efficiently - access the string values inside of array structures without resorting - to per-row type inspection - - * When used with the Python ``getitem`` accessor, the number of dimensions - serves to define the kind of type that the ``[]`` operator should - return, e.g. for an ARRAY of INTEGER with two dimensions:: - - >>> expr = table.c.column[5] # returns ARRAY(Integer, dimensions=1) - >>> expr = expr[6] # returns Integer - - For 1-dimensional arrays, an :class:`_types.ARRAY` instance with no - dimension parameter will generally assume single-dimensional behaviors. - - SQL expressions of type :class:`_types.ARRAY` have support for "index" and - "slice" behavior. The ``[]`` operator produces expression - constructs which will produce the appropriate SQL, both for - SELECT statements:: - - select(mytable.c.data[5], mytable.c.data[2:7]) - - as well as UPDATE statements when the :meth:`_expression.Update.values` - method is used:: - - mytable.update().values({ - mytable.c.data[5]: 7, - mytable.c.data[2:7]: [1, 2, 3] - }) - - Indexed access is one-based by default; - for zero-based index conversion, set :paramref:`_types.ARRAY.zero_indexes`. - - The :class:`_types.ARRAY` type also provides for the operators - :meth:`.types.ARRAY.Comparator.any` and - :meth:`.types.ARRAY.Comparator.all`. The PostgreSQL-specific version of - :class:`_types.ARRAY` also provides additional operators. - - .. container:: topic - - **Detecting Changes in ARRAY columns when using the ORM** - - The :class:`_sqltypes.ARRAY` type, when used with the SQLAlchemy ORM, - does not detect in-place mutations to the array. In order to detect - these, the :mod:`sqlalchemy.ext.mutable` extension must be used, using - the :class:`.MutableList` class:: - - from sqlalchemy import ARRAY - from sqlalchemy.ext.mutable import MutableList - - class SomeOrmClass(Base): - # ... - - data = Column(MutableList.as_mutable(ARRAY(Integer))) - - This extension will allow "in-place" changes such to the array - such as ``.append()`` to produce events which will be detected by the - unit of work. Note that changes to elements **inside** the array, - including subarrays that are mutated in place, are **not** detected. - - Alternatively, assigning a new array value to an ORM element that - replaces the old one will always trigger a change event. - - .. seealso:: - - :class:`sqlalchemy.dialects.postgresql.ARRAY` - - """ - - __visit_name__ = "ARRAY" - - _is_array = True - - zero_indexes = False - """If True, Python zero-based indexes should be interpreted as one-based - on the SQL expression side.""" - - def __init__( - self, - item_type: _TypeEngineArgument[Any], - as_tuple: bool = False, - dimensions: Optional[int] = None, - zero_indexes: bool = False, - ): - """Construct an :class:`_types.ARRAY`. - - E.g.:: - - Column('myarray', ARRAY(Integer)) - - Arguments are: - - :param item_type: The data type of items of this array. Note that - dimensionality is irrelevant here, so multi-dimensional arrays like - ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as - ``ARRAY(ARRAY(Integer))`` or such. - - :param as_tuple=False: Specify whether return results - should be converted to tuples from lists. This parameter is - not generally needed as a Python list corresponds well - to a SQL array. - - :param dimensions: if non-None, the ARRAY will assume a fixed - number of dimensions. This impacts how the array is declared - on the database, how it goes about interpreting Python and - result values, as well as how expression behavior in conjunction - with the "getitem" operator works. See the description at - :class:`_types.ARRAY` for additional detail. - - :param zero_indexes=False: when True, index values will be converted - between Python zero-based and SQL one-based indexes, e.g. - a value of one will be added to all index values before passing - to the database. - - """ - if isinstance(item_type, ARRAY): - raise ValueError( - "Do not nest ARRAY types; ARRAY(basetype) " - "handles multi-dimensional arrays of basetype" - ) - if isinstance(item_type, type): - item_type = item_type() - self.item_type = item_type - self.as_tuple = as_tuple - self.dimensions = dimensions - self.zero_indexes = zero_indexes - - class Comparator( - Indexable.Comparator[Sequence[Any]], - Concatenable.Comparator[Sequence[Any]], - ): - """Define comparison operations for :class:`_types.ARRAY`. - - More operators are available on the dialect-specific form - of this type. See :class:`.postgresql.ARRAY.Comparator`. - - """ - - __slots__ = () - - type: ARRAY - - def _setup_getitem(self, index): - arr_type = self.type - - return_type: TypeEngine[Any] - - if isinstance(index, slice): - return_type = arr_type - if arr_type.zero_indexes: - index = slice(index.start + 1, index.stop + 1, index.step) - slice_ = Slice( - index.start, index.stop, index.step, _name=self.expr.key - ) - return operators.getitem, slice_, return_type - else: - if arr_type.zero_indexes: - index += 1 - if arr_type.dimensions is None or arr_type.dimensions == 1: - return_type = arr_type.item_type - else: - adapt_kw = {"dimensions": arr_type.dimensions - 1} - return_type = arr_type.adapt( - arr_type.__class__, **adapt_kw - ) - - return operators.getitem, index, return_type - - def contains(self, *arg, **kw): - """``ARRAY.contains()`` not implemented for the base ARRAY type. - Use the dialect-specific ARRAY type. - - .. seealso:: - - :class:`_postgresql.ARRAY` - PostgreSQL specific version. - """ - raise NotImplementedError( - "ARRAY.contains() not implemented for the base " - "ARRAY type; please use the dialect-specific ARRAY type" - ) - - @util.preload_module("sqlalchemy.sql.elements") - def any(self, other, operator=None): - """Return ``other operator ANY (array)`` clause. - - .. legacy:: This method is an :class:`_types.ARRAY` - specific - construct that is now superseded by the :func:`_sql.any_` - function, which features a different calling style. The - :func:`_sql.any_` function is also mirrored at the method level - via the :meth:`_sql.ColumnOperators.any_` method. - - Usage of array-specific :meth:`_types.ARRAY.Comparator.any` - is as follows:: - - from sqlalchemy.sql import operators - - conn.execute( - select(table.c.data).where( - table.c.data.any(7, operator=operators.lt) - ) - ) - - :param other: expression to be compared - :param operator: an operator object from the - :mod:`sqlalchemy.sql.operators` - package, defaults to :func:`.operators.eq`. - - .. seealso:: - - :func:`_expression.any_` - - :meth:`.types.ARRAY.Comparator.all` - - """ - elements = util.preloaded.sql_elements - operator = operator if operator else operators.eq - - arr_type = self.type - - return elements.CollectionAggregate._create_any(self.expr).operate( - operators.mirror(operator), - coercions.expect( - roles.BinaryElementRole, - element=other, - operator=operator, - expr=self.expr, - bindparam_type=arr_type.item_type, - ), - ) - - @util.preload_module("sqlalchemy.sql.elements") - def all(self, other, operator=None): - """Return ``other operator ALL (array)`` clause. - - .. legacy:: This method is an :class:`_types.ARRAY` - specific - construct that is now superseded by the :func:`_sql.all_` - function, which features a different calling style. The - :func:`_sql.all_` function is also mirrored at the method level - via the :meth:`_sql.ColumnOperators.all_` method. - - Usage of array-specific :meth:`_types.ARRAY.Comparator.all` - is as follows:: - - from sqlalchemy.sql import operators - - conn.execute( - select(table.c.data).where( - table.c.data.all(7, operator=operators.lt) - ) - ) - - :param other: expression to be compared - :param operator: an operator object from the - :mod:`sqlalchemy.sql.operators` - package, defaults to :func:`.operators.eq`. - - .. seealso:: - - :func:`_expression.all_` - - :meth:`.types.ARRAY.Comparator.any` - - """ - elements = util.preloaded.sql_elements - operator = operator if operator else operators.eq - - arr_type = self.type - - return elements.CollectionAggregate._create_all(self.expr).operate( - operators.mirror(operator), - coercions.expect( - roles.BinaryElementRole, - element=other, - operator=operator, - expr=self.expr, - bindparam_type=arr_type.item_type, - ), - ) - - comparator_factory = Comparator - - @property - def hashable(self): - return self.as_tuple - - @property - def python_type(self): - return list - - def compare_values(self, x, y): - return x == y - - def _set_parent(self, column, outer=False, **kw): - """Support SchemaEventTarget""" - - if not outer and isinstance(self.item_type, SchemaEventTarget): - self.item_type._set_parent(column, **kw) - - def _set_parent_with_dispatch(self, parent): - """Support SchemaEventTarget""" - - super()._set_parent_with_dispatch(parent, outer=True) - - if isinstance(self.item_type, SchemaEventTarget): - self.item_type._set_parent_with_dispatch(parent) - - def literal_processor(self, dialect): - item_proc = self.item_type.dialect_impl(dialect).literal_processor( - dialect - ) - if item_proc is None: - return None - - def to_str(elements): - return f"[{', '.join(elements)}]" - - def process(value): - inner = self._apply_item_processor( - value, item_proc, self.dimensions, to_str - ) - return inner - - return process - - def _apply_item_processor(self, arr, itemproc, dim, collection_callable): - """Helper method that can be used by bind_processor(), - literal_processor(), etc. to apply an item processor to elements of - an array value, taking into account the 'dimensions' for this - array type. - - See the Postgresql ARRAY datatype for usage examples. - - .. versionadded:: 2.0 - - """ - - if dim is None: - arr = list(arr) - if ( - dim == 1 - or dim is None - and ( - # this has to be (list, tuple), or at least - # not hasattr('__iter__'), since Py3K strings - # etc. have __iter__ - not arr - or not isinstance(arr[0], (list, tuple)) - ) - ): - if itemproc: - return collection_callable(itemproc(x) for x in arr) - else: - return collection_callable(arr) - else: - return collection_callable( - ( - self._apply_item_processor( - x, - itemproc, - dim - 1 if dim is not None else None, - collection_callable, - ) - if x is not None - else None - ) - for x in arr - ) - - -class TupleType(TypeEngine[Tuple[Any, ...]]): - """represent the composite type of a Tuple.""" - - _is_tuple_type = True - - types: List[TypeEngine[Any]] - - def __init__(self, *types: _TypeEngineArgument[Any]): - self._fully_typed = NULLTYPE not in types - self.types = [ - item_type() if isinstance(item_type, type) else item_type - for item_type in types - ] - - def coerce_compared_value( - self, op: Optional[OperatorType], value: Any - ) -> TypeEngine[Any]: - if value is type_api._NO_VALUE_IN_LIST: - return super().coerce_compared_value(op, value) - else: - return TupleType( - *[ - typ.coerce_compared_value(op, elem) - for typ, elem in zip(self.types, value) - ] - ) - - def _resolve_values_to_types(self, value: Any) -> TupleType: - if self._fully_typed: - return self - else: - return TupleType( - *[ - _resolve_value_to_type(elem) if typ is NULLTYPE else typ - for typ, elem in zip(self.types, value) - ] - ) - - def result_processor(self, dialect, coltype): - raise NotImplementedError( - "The tuple type does not support being fetched " - "as a column in a result row." - ) - - -class REAL(Float[_N]): - """The SQL REAL type. - - .. seealso:: - - :class:`_types.Float` - documentation for the base type. - - """ - - __visit_name__ = "REAL" - - -class FLOAT(Float[_N]): - """The SQL FLOAT type. - - .. seealso:: - - :class:`_types.Float` - documentation for the base type. - - """ - - __visit_name__ = "FLOAT" - - -class DOUBLE(Double[_N]): - """The SQL DOUBLE type. - - .. versionadded:: 2.0 - - .. seealso:: - - :class:`_types.Double` - documentation for the base type. - - """ - - __visit_name__ = "DOUBLE" - - -class DOUBLE_PRECISION(Double[_N]): - """The SQL DOUBLE PRECISION type. - - .. versionadded:: 2.0 - - .. seealso:: - - :class:`_types.Double` - documentation for the base type. - - """ - - __visit_name__ = "DOUBLE_PRECISION" - - -class NUMERIC(Numeric[_N]): - """The SQL NUMERIC type. - - .. seealso:: - - :class:`_types.Numeric` - documentation for the base type. - - """ - - __visit_name__ = "NUMERIC" - - -class DECIMAL(Numeric[_N]): - """The SQL DECIMAL type. - - .. seealso:: - - :class:`_types.Numeric` - documentation for the base type. - - """ - - __visit_name__ = "DECIMAL" - - -class INTEGER(Integer): - """The SQL INT or INTEGER type. - - .. seealso:: - - :class:`_types.Integer` - documentation for the base type. - - """ - - __visit_name__ = "INTEGER" - - -INT = INTEGER - - -class SMALLINT(SmallInteger): - """The SQL SMALLINT type. - - .. seealso:: - - :class:`_types.SmallInteger` - documentation for the base type. - - """ - - __visit_name__ = "SMALLINT" - - -class BIGINT(BigInteger): - """The SQL BIGINT type. - - .. seealso:: - - :class:`_types.BigInteger` - documentation for the base type. - - """ - - __visit_name__ = "BIGINT" - - -class TIMESTAMP(DateTime): - """The SQL TIMESTAMP type. - - :class:`_types.TIMESTAMP` datatypes have support for timezone - storage on some backends, such as PostgreSQL and Oracle. Use the - :paramref:`~types.TIMESTAMP.timezone` argument in order to enable - "TIMESTAMP WITH TIMEZONE" for these backends. - - """ - - __visit_name__ = "TIMESTAMP" - - def __init__(self, timezone: bool = False): - """Construct a new :class:`_types.TIMESTAMP`. - - :param timezone: boolean. Indicates that the TIMESTAMP type should - enable timezone support, if available on the target database. - On a per-dialect basis is similar to "TIMESTAMP WITH TIMEZONE". - If the target database does not support timezones, this flag is - ignored. - - - """ - super().__init__(timezone=timezone) - - def get_dbapi_type(self, dbapi): - return dbapi.TIMESTAMP - - -class DATETIME(DateTime): - """The SQL DATETIME type.""" - - __visit_name__ = "DATETIME" - - -class DATE(Date): - """The SQL DATE type.""" - - __visit_name__ = "DATE" - - -class TIME(Time): - """The SQL TIME type.""" - - __visit_name__ = "TIME" - - -class TEXT(Text): - """The SQL TEXT type.""" - - __visit_name__ = "TEXT" - - -class CLOB(Text): - """The CLOB type. - - This type is found in Oracle and Informix. - """ - - __visit_name__ = "CLOB" - - -class VARCHAR(String): - """The SQL VARCHAR type.""" - - __visit_name__ = "VARCHAR" - - -class NVARCHAR(Unicode): - """The SQL NVARCHAR type.""" - - __visit_name__ = "NVARCHAR" - - -class CHAR(String): - """The SQL CHAR type.""" - - __visit_name__ = "CHAR" - - -class NCHAR(Unicode): - """The SQL NCHAR type.""" - - __visit_name__ = "NCHAR" - - -class BLOB(LargeBinary): - """The SQL BLOB type.""" - - __visit_name__ = "BLOB" - - -class BINARY(_Binary): - """The SQL BINARY type.""" - - __visit_name__ = "BINARY" - - -class VARBINARY(_Binary): - """The SQL VARBINARY type.""" - - __visit_name__ = "VARBINARY" - - -class BOOLEAN(Boolean): - """The SQL BOOLEAN type.""" - - __visit_name__ = "BOOLEAN" - - -class NullType(TypeEngine[None]): - """An unknown type. - - :class:`.NullType` is used as a default type for those cases where - a type cannot be determined, including: - - * During table reflection, when the type of a column is not recognized - by the :class:`.Dialect` - * When constructing SQL expressions using plain Python objects of - unknown types (e.g. ``somecolumn == my_special_object``) - * When a new :class:`_schema.Column` is created, - and the given type is passed - as ``None`` or is not passed at all. - - The :class:`.NullType` can be used within SQL expression invocation - without issue, it just has no behavior either at the expression - construction level or at the bind-parameter/result processing level. - :class:`.NullType` will result in a :exc:`.CompileError` if the compiler - is asked to render the type itself, such as if it is used in a - :func:`.cast` operation or within a schema creation operation such as that - invoked by :meth:`_schema.MetaData.create_all` or the - :class:`.CreateTable` - construct. - - """ - - __visit_name__ = "null" - - _isnull = True - - def literal_processor(self, dialect): - return None - - class Comparator(TypeEngine.Comparator[_T]): - __slots__ = () - - def _adapt_expression( - self, - op: OperatorType, - other_comparator: TypeEngine.Comparator[Any], - ) -> Tuple[OperatorType, TypeEngine[Any]]: - if isinstance( - other_comparator, NullType.Comparator - ) or not operators.is_commutative(op): - return op, self.expr.type - else: - return other_comparator._adapt_expression(op, self) - - comparator_factory = Comparator - - -class TableValueType(HasCacheKey, TypeEngine[Any]): - """Refers to a table value type.""" - - _is_table_value = True - - _traverse_internals = [ - ("_elements", InternalTraversal.dp_clauseelement_list), - ] - - def __init__(self, *elements: Union[str, _ColumnExpressionArgument[Any]]): - self._elements = [ - coercions.expect(roles.StrAsPlainColumnRole, elem) - for elem in elements - ] - - -class MatchType(Boolean): - """Refers to the return type of the MATCH operator. - - As the :meth:`.ColumnOperators.match` is probably the most open-ended - operator in generic SQLAlchemy Core, we can't assume the return type - at SQL evaluation time, as MySQL returns a floating point, not a boolean, - and other backends might do something different. So this type - acts as a placeholder, currently subclassing :class:`.Boolean`. - The type allows dialects to inject result-processing functionality - if needed, and on MySQL will return floating-point values. - - """ - - -_UUID_RETURN = TypeVar("_UUID_RETURN", str, _python_UUID) - - -class Uuid(Emulated, TypeEngine[_UUID_RETURN]): - """Represent a database agnostic UUID datatype. - - For backends that have no "native" UUID datatype, the value will - make use of ``CHAR(32)`` and store the UUID as a 32-character alphanumeric - hex string. - - For backends which are known to support ``UUID`` directly or a similar - uuid-storing datatype such as SQL Server's ``UNIQUEIDENTIFIER``, a - "native" mode enabled by default allows these types will be used on those - backends. - - In its default mode of use, the :class:`_sqltypes.Uuid` datatype expects - **Python uuid objects**, from the Python - `uuid <https://docs.python.org/3/library/uuid.html>`_ - module:: - - import uuid - - from sqlalchemy import Uuid - from sqlalchemy import Table, Column, MetaData, String - - - metadata_obj = MetaData() - - t = Table( - "t", - metadata_obj, - Column('uuid_data', Uuid, primary_key=True), - Column("other_data", String) - ) - - with engine.begin() as conn: - conn.execute( - t.insert(), - {"uuid_data": uuid.uuid4(), "other_data", "some data"} - ) - - To have the :class:`_sqltypes.Uuid` datatype work with string-based - Uuids (e.g. 32 character hexadecimal strings), pass the - :paramref:`_sqltypes.Uuid.as_uuid` parameter with the value ``False``. - - .. versionadded:: 2.0 - - .. seealso:: - - :class:`_sqltypes.UUID` - represents exactly the ``UUID`` datatype - without any backend-agnostic behaviors. - - """ - - __visit_name__ = "uuid" - - collation: Optional[str] = None - - @overload - def __init__( - self: Uuid[_python_UUID], - as_uuid: Literal[True] = ..., - native_uuid: bool = ..., - ): ... - - @overload - def __init__( - self: Uuid[str], - as_uuid: Literal[False] = ..., - native_uuid: bool = ..., - ): ... - - def __init__(self, as_uuid: bool = True, native_uuid: bool = True): - """Construct a :class:`_sqltypes.Uuid` type. - - :param as_uuid=True: if True, values will be interpreted - as Python uuid objects, converting to/from string via the - DBAPI. - - .. versionchanged: 2.0 ``as_uuid`` now defaults to ``True``. - - :param native_uuid=True: if True, backends that support either the - ``UUID`` datatype directly, or a UUID-storing value - (such as SQL Server's ``UNIQUEIDENTIFIER`` will be used by those - backends. If False, a ``CHAR(32)`` datatype will be used for - all backends regardless of native support. - - """ - self.as_uuid = as_uuid - self.native_uuid = native_uuid - - @property - def python_type(self): - return _python_UUID if self.as_uuid else str - - @property - def native(self): - return self.native_uuid - - def coerce_compared_value(self, op, value): - """See :meth:`.TypeEngine.coerce_compared_value` for a description.""" - - if isinstance(value, str): - return self - else: - return super().coerce_compared_value(op, value) - - def bind_processor(self, dialect): - character_based_uuid = ( - not dialect.supports_native_uuid or not self.native_uuid - ) - - if character_based_uuid: - if self.as_uuid: - - def process(value): - if value is not None: - value = value.hex - return value - - return process - else: - - def process(value): - if value is not None: - value = value.replace("-", "") - return value - - return process - else: - return None - - def result_processor(self, dialect, coltype): - character_based_uuid = ( - not dialect.supports_native_uuid or not self.native_uuid - ) - - if character_based_uuid: - if self.as_uuid: - - def process(value): - if value is not None: - value = _python_UUID(value) - return value - - return process - else: - - def process(value): - if value is not None: - value = str(_python_UUID(value)) - return value - - return process - else: - if not self.as_uuid: - - def process(value): - if value is not None: - value = str(value) - return value - - return process - else: - return None - - def literal_processor(self, dialect): - character_based_uuid = ( - not dialect.supports_native_uuid or not self.native_uuid - ) - - if not self.as_uuid: - - def process(value): - return f"""'{value.replace("-", "").replace("'", "''")}'""" - - return process - else: - if character_based_uuid: - - def process(value): - return f"""'{value.hex}'""" - - return process - else: - - def process(value): - return f"""'{str(value).replace("'", "''")}'""" - - return process - - -class UUID(Uuid[_UUID_RETURN], type_api.NativeForEmulated): - """Represent the SQL UUID type. - - This is the SQL-native form of the :class:`_types.Uuid` database agnostic - datatype, and is backwards compatible with the previous PostgreSQL-only - version of ``UUID``. - - The :class:`_sqltypes.UUID` datatype only works on databases that have a - SQL datatype named ``UUID``. It will not function for backends which don't - have this exact-named type, including SQL Server. For backend-agnostic UUID - values with native support, including for SQL Server's ``UNIQUEIDENTIFIER`` - datatype, use the :class:`_sqltypes.Uuid` datatype. - - .. versionadded:: 2.0 - - .. seealso:: - - :class:`_sqltypes.Uuid` - - """ - - __visit_name__ = "UUID" - - @overload - def __init__(self: UUID[_python_UUID], as_uuid: Literal[True] = ...): ... - - @overload - def __init__(self: UUID[str], as_uuid: Literal[False] = ...): ... - - def __init__(self, as_uuid: bool = True): - """Construct a :class:`_sqltypes.UUID` type. - - - :param as_uuid=True: if True, values will be interpreted - as Python uuid objects, converting to/from string via the - DBAPI. - - .. versionchanged: 2.0 ``as_uuid`` now defaults to ``True``. - - """ - self.as_uuid = as_uuid - self.native_uuid = True - - @classmethod - def adapt_emulated_to_native(cls, impl, **kw): - kw.setdefault("as_uuid", impl.as_uuid) - return cls(**kw) - - -NULLTYPE = NullType() -BOOLEANTYPE = Boolean() -STRINGTYPE = String() -INTEGERTYPE = Integer() -NUMERICTYPE: Numeric[decimal.Decimal] = Numeric() -MATCHTYPE = MatchType() -TABLEVALUE = TableValueType() -DATETIME_TIMEZONE = DateTime(timezone=True) -TIME_TIMEZONE = Time(timezone=True) -_BIGINTEGER = BigInteger() -_DATETIME = DateTime() -_TIME = Time() -_STRING = String() -_UNICODE = Unicode() - -_type_map: Dict[Type[Any], TypeEngine[Any]] = { - int: Integer(), - float: Float(), - bool: BOOLEANTYPE, - _python_UUID: Uuid(), - decimal.Decimal: Numeric(), - dt.date: Date(), - dt.datetime: _DATETIME, - dt.time: _TIME, - dt.timedelta: Interval(), - type(None): NULLTYPE, - bytes: LargeBinary(), - str: _STRING, - enum.Enum: Enum(enum.Enum), - Literal: Enum(enum.Enum), # type: ignore[dict-item] -} - - -_type_map_get = _type_map.get - - -def _resolve_value_to_type(value: Any) -> TypeEngine[Any]: - _result_type = _type_map_get(type(value), False) - - if _result_type is False: - _result_type = getattr(value, "__sa_type_engine__", False) - - if _result_type is False: - # use inspect() to detect SQLAlchemy built-in - # objects. - insp = inspection.inspect(value, False) - if ( - insp is not None - and - # foil mock.Mock() and other impostors by ensuring - # the inspection target itself self-inspects - insp.__class__ in inspection._registrars - ): - raise exc.ArgumentError( - "Object %r is not legal as a SQL literal value" % (value,) - ) - return NULLTYPE - else: - return _result_type._resolve_for_literal( # type: ignore [union-attr] - value - ) - - -# back-assign to type_api -type_api.BOOLEANTYPE = BOOLEANTYPE -type_api.STRINGTYPE = STRINGTYPE -type_api.INTEGERTYPE = INTEGERTYPE -type_api.NULLTYPE = NULLTYPE -type_api.NUMERICTYPE = NUMERICTYPE -type_api.MATCHTYPE = MATCHTYPE -type_api.INDEXABLE = INDEXABLE = Indexable -type_api.TABLEVALUE = TABLEVALUE -type_api._resolve_value_to_type = _resolve_value_to_type diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/traversals.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/traversals.py deleted file mode 100644 index 3ca3caf..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/traversals.py +++ /dev/null @@ -1,1022 +0,0 @@ -# sql/traversals.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -from __future__ import annotations - -from collections import deque -import collections.abc as collections_abc -import itertools -from itertools import zip_longest -import operator -import typing -from typing import Any -from typing import Callable -from typing import Deque -from typing import Dict -from typing import Iterable -from typing import Optional -from typing import Set -from typing import Tuple -from typing import Type - -from . import operators -from .cache_key import HasCacheKey -from .visitors import _TraverseInternalsType -from .visitors import anon_map -from .visitors import ExternallyTraversible -from .visitors import HasTraversalDispatch -from .visitors import HasTraverseInternals -from .. import util -from ..util import langhelpers -from ..util.typing import Self - - -SKIP_TRAVERSE = util.symbol("skip_traverse") -COMPARE_FAILED = False -COMPARE_SUCCEEDED = True - - -def compare(obj1: Any, obj2: Any, **kw: Any) -> bool: - strategy: TraversalComparatorStrategy - if kw.get("use_proxies", False): - strategy = ColIdentityComparatorStrategy() - else: - strategy = TraversalComparatorStrategy() - - return strategy.compare(obj1, obj2, **kw) - - -def _preconfigure_traversals(target_hierarchy: Type[Any]) -> None: - for cls in util.walk_subclasses(target_hierarchy): - if hasattr(cls, "_generate_cache_attrs") and hasattr( - cls, "_traverse_internals" - ): - cls._generate_cache_attrs() - _copy_internals.generate_dispatch( - cls, - cls._traverse_internals, - "_generated_copy_internals_traversal", - ) - _get_children.generate_dispatch( - cls, - cls._traverse_internals, - "_generated_get_children_traversal", - ) - - -class HasShallowCopy(HasTraverseInternals): - """attribute-wide operations that are useful for classes that use - __slots__ and therefore can't operate on their attributes in a dictionary. - - - """ - - __slots__ = () - - if typing.TYPE_CHECKING: - - def _generated_shallow_copy_traversal(self, other: Self) -> None: ... - - def _generated_shallow_from_dict_traversal( - self, d: Dict[str, Any] - ) -> None: ... - - def _generated_shallow_to_dict_traversal(self) -> Dict[str, Any]: ... - - @classmethod - def _generate_shallow_copy( - cls, - internal_dispatch: _TraverseInternalsType, - method_name: str, - ) -> Callable[[Self, Self], None]: - code = "\n".join( - f" other.{attrname} = self.{attrname}" - for attrname, _ in internal_dispatch - ) - meth_text = f"def {method_name}(self, other):\n{code}\n" - return langhelpers._exec_code_in_env(meth_text, {}, method_name) - - @classmethod - def _generate_shallow_to_dict( - cls, - internal_dispatch: _TraverseInternalsType, - method_name: str, - ) -> Callable[[Self], Dict[str, Any]]: - code = ",\n".join( - f" '{attrname}': self.{attrname}" - for attrname, _ in internal_dispatch - ) - meth_text = f"def {method_name}(self):\n return {{{code}}}\n" - return langhelpers._exec_code_in_env(meth_text, {}, method_name) - - @classmethod - def _generate_shallow_from_dict( - cls, - internal_dispatch: _TraverseInternalsType, - method_name: str, - ) -> Callable[[Self, Dict[str, Any]], None]: - code = "\n".join( - f" self.{attrname} = d['{attrname}']" - for attrname, _ in internal_dispatch - ) - meth_text = f"def {method_name}(self, d):\n{code}\n" - return langhelpers._exec_code_in_env(meth_text, {}, method_name) - - def _shallow_from_dict(self, d: Dict[str, Any]) -> None: - cls = self.__class__ - - shallow_from_dict: Callable[[HasShallowCopy, Dict[str, Any]], None] - try: - shallow_from_dict = cls.__dict__[ - "_generated_shallow_from_dict_traversal" - ] - except KeyError: - shallow_from_dict = self._generate_shallow_from_dict( - cls._traverse_internals, - "_generated_shallow_from_dict_traversal", - ) - - cls._generated_shallow_from_dict_traversal = shallow_from_dict # type: ignore # noqa: E501 - - shallow_from_dict(self, d) - - def _shallow_to_dict(self) -> Dict[str, Any]: - cls = self.__class__ - - shallow_to_dict: Callable[[HasShallowCopy], Dict[str, Any]] - - try: - shallow_to_dict = cls.__dict__[ - "_generated_shallow_to_dict_traversal" - ] - except KeyError: - shallow_to_dict = self._generate_shallow_to_dict( - cls._traverse_internals, "_generated_shallow_to_dict_traversal" - ) - - cls._generated_shallow_to_dict_traversal = shallow_to_dict # type: ignore # noqa: E501 - return shallow_to_dict(self) - - def _shallow_copy_to(self, other: Self) -> None: - cls = self.__class__ - - shallow_copy: Callable[[Self, Self], None] - try: - shallow_copy = cls.__dict__["_generated_shallow_copy_traversal"] - except KeyError: - shallow_copy = self._generate_shallow_copy( - cls._traverse_internals, "_generated_shallow_copy_traversal" - ) - - cls._generated_shallow_copy_traversal = shallow_copy # type: ignore # noqa: E501 - shallow_copy(self, other) - - def _clone(self, **kw: Any) -> Self: - """Create a shallow copy""" - c = self.__class__.__new__(self.__class__) - self._shallow_copy_to(c) - return c - - -class GenerativeOnTraversal(HasShallowCopy): - """Supplies Generative behavior but making use of traversals to shallow - copy. - - .. seealso:: - - :class:`sqlalchemy.sql.base.Generative` - - - """ - - __slots__ = () - - def _generate(self) -> Self: - cls = self.__class__ - s = cls.__new__(cls) - self._shallow_copy_to(s) - return s - - -def _clone(element, **kw): - return element._clone() - - -class HasCopyInternals(HasTraverseInternals): - __slots__ = () - - def _clone(self, **kw): - raise NotImplementedError() - - def _copy_internals( - self, *, omit_attrs: Iterable[str] = (), **kw: Any - ) -> None: - """Reassign internal elements to be clones of themselves. - - Called during a copy-and-traverse operation on newly - shallow-copied elements to create a deep copy. - - The given clone function should be used, which may be applying - additional transformations to the element (i.e. replacement - traversal, cloned traversal, annotations). - - """ - - try: - traverse_internals = self._traverse_internals - except AttributeError: - # user-defined classes may not have a _traverse_internals - return - - for attrname, obj, meth in _copy_internals.run_generated_dispatch( - self, traverse_internals, "_generated_copy_internals_traversal" - ): - if attrname in omit_attrs: - continue - - if obj is not None: - result = meth(attrname, self, obj, **kw) - if result is not None: - setattr(self, attrname, result) - - -class _CopyInternalsTraversal(HasTraversalDispatch): - """Generate a _copy_internals internal traversal dispatch for classes - with a _traverse_internals collection.""" - - def visit_clauseelement( - self, attrname, parent, element, clone=_clone, **kw - ): - return clone(element, **kw) - - def visit_clauseelement_list( - self, attrname, parent, element, clone=_clone, **kw - ): - return [clone(clause, **kw) for clause in element] - - def visit_clauseelement_tuple( - self, attrname, parent, element, clone=_clone, **kw - ): - return tuple([clone(clause, **kw) for clause in element]) - - def visit_executable_options( - self, attrname, parent, element, clone=_clone, **kw - ): - return tuple([clone(clause, **kw) for clause in element]) - - def visit_clauseelement_unordered_set( - self, attrname, parent, element, clone=_clone, **kw - ): - return {clone(clause, **kw) for clause in element} - - def visit_clauseelement_tuples( - self, attrname, parent, element, clone=_clone, **kw - ): - return [ - tuple(clone(tup_elem, **kw) for tup_elem in elem) - for elem in element - ] - - def visit_string_clauseelement_dict( - self, attrname, parent, element, clone=_clone, **kw - ): - return {key: clone(value, **kw) for key, value in element.items()} - - def visit_setup_join_tuple( - self, attrname, parent, element, clone=_clone, **kw - ): - return tuple( - ( - clone(target, **kw) if target is not None else None, - clone(onclause, **kw) if onclause is not None else None, - clone(from_, **kw) if from_ is not None else None, - flags, - ) - for (target, onclause, from_, flags) in element - ) - - def visit_memoized_select_entities(self, attrname, parent, element, **kw): - return self.visit_clauseelement_tuple(attrname, parent, element, **kw) - - def visit_dml_ordered_values( - self, attrname, parent, element, clone=_clone, **kw - ): - # sequence of 2-tuples - return [ - ( - ( - clone(key, **kw) - if hasattr(key, "__clause_element__") - else key - ), - clone(value, **kw), - ) - for key, value in element - ] - - def visit_dml_values(self, attrname, parent, element, clone=_clone, **kw): - return { - ( - clone(key, **kw) if hasattr(key, "__clause_element__") else key - ): clone(value, **kw) - for key, value in element.items() - } - - def visit_dml_multi_values( - self, attrname, parent, element, clone=_clone, **kw - ): - # sequence of sequences, each sequence contains a list/dict/tuple - - def copy(elem): - if isinstance(elem, (list, tuple)): - return [ - ( - clone(value, **kw) - if hasattr(value, "__clause_element__") - else value - ) - for value in elem - ] - elif isinstance(elem, dict): - return { - ( - clone(key, **kw) - if hasattr(key, "__clause_element__") - else key - ): ( - clone(value, **kw) - if hasattr(value, "__clause_element__") - else value - ) - for key, value in elem.items() - } - else: - # TODO: use abc classes - assert False - - return [ - [copy(sub_element) for sub_element in sequence] - for sequence in element - ] - - def visit_propagate_attrs( - self, attrname, parent, element, clone=_clone, **kw - ): - return element - - -_copy_internals = _CopyInternalsTraversal() - - -def _flatten_clauseelement(element): - while hasattr(element, "__clause_element__") and not getattr( - element, "is_clause_element", False - ): - element = element.__clause_element__() - - return element - - -class _GetChildrenTraversal(HasTraversalDispatch): - """Generate a _children_traversal internal traversal dispatch for classes - with a _traverse_internals collection.""" - - def visit_has_cache_key(self, element, **kw): - # the GetChildren traversal refers explicitly to ClauseElement - # structures. Within these, a plain HasCacheKey is not a - # ClauseElement, so don't include these. - return () - - def visit_clauseelement(self, element, **kw): - return (element,) - - def visit_clauseelement_list(self, element, **kw): - return element - - def visit_clauseelement_tuple(self, element, **kw): - return element - - def visit_clauseelement_tuples(self, element, **kw): - return itertools.chain.from_iterable(element) - - def visit_fromclause_canonical_column_collection(self, element, **kw): - return () - - def visit_string_clauseelement_dict(self, element, **kw): - return element.values() - - def visit_fromclause_ordered_set(self, element, **kw): - return element - - def visit_clauseelement_unordered_set(self, element, **kw): - return element - - def visit_setup_join_tuple(self, element, **kw): - for target, onclause, from_, flags in element: - if from_ is not None: - yield from_ - - if not isinstance(target, str): - yield _flatten_clauseelement(target) - - if onclause is not None and not isinstance(onclause, str): - yield _flatten_clauseelement(onclause) - - def visit_memoized_select_entities(self, element, **kw): - return self.visit_clauseelement_tuple(element, **kw) - - def visit_dml_ordered_values(self, element, **kw): - for k, v in element: - if hasattr(k, "__clause_element__"): - yield k - yield v - - def visit_dml_values(self, element, **kw): - expr_values = {k for k in element if hasattr(k, "__clause_element__")} - str_values = expr_values.symmetric_difference(element) - - for k in sorted(str_values): - yield element[k] - for k in expr_values: - yield k - yield element[k] - - def visit_dml_multi_values(self, element, **kw): - return () - - def visit_propagate_attrs(self, element, **kw): - return () - - -_get_children = _GetChildrenTraversal() - - -@util.preload_module("sqlalchemy.sql.elements") -def _resolve_name_for_compare(element, name, anon_map, **kw): - if isinstance(name, util.preloaded.sql_elements._anonymous_label): - name = name.apply_map(anon_map) - - return name - - -class TraversalComparatorStrategy(HasTraversalDispatch, util.MemoizedSlots): - __slots__ = "stack", "cache", "anon_map" - - def __init__(self): - self.stack: Deque[ - Tuple[ - Optional[ExternallyTraversible], - Optional[ExternallyTraversible], - ] - ] = deque() - self.cache = set() - - def _memoized_attr_anon_map(self): - return (anon_map(), anon_map()) - - def compare( - self, - obj1: ExternallyTraversible, - obj2: ExternallyTraversible, - **kw: Any, - ) -> bool: - stack = self.stack - cache = self.cache - - compare_annotations = kw.get("compare_annotations", False) - - stack.append((obj1, obj2)) - - while stack: - left, right = stack.popleft() - - if left is right: - continue - elif left is None or right is None: - # we know they are different so no match - return False - elif (left, right) in cache: - continue - cache.add((left, right)) - - visit_name = left.__visit_name__ - if visit_name != right.__visit_name__: - return False - - meth = getattr(self, "compare_%s" % visit_name, None) - - if meth: - attributes_compared = meth(left, right, **kw) - if attributes_compared is COMPARE_FAILED: - return False - elif attributes_compared is SKIP_TRAVERSE: - continue - - # attributes_compared is returned as a list of attribute - # names that were "handled" by the comparison method above. - # remaining attribute names in the _traverse_internals - # will be compared. - else: - attributes_compared = () - - for ( - (left_attrname, left_visit_sym), - (right_attrname, right_visit_sym), - ) in zip_longest( - left._traverse_internals, - right._traverse_internals, - fillvalue=(None, None), - ): - if not compare_annotations and ( - (left_attrname == "_annotations") - or (right_attrname == "_annotations") - ): - continue - - if ( - left_attrname != right_attrname - or left_visit_sym is not right_visit_sym - ): - return False - elif left_attrname in attributes_compared: - continue - - assert left_visit_sym is not None - assert left_attrname is not None - assert right_attrname is not None - - dispatch = self.dispatch(left_visit_sym) - assert dispatch is not None, ( - f"{self.__class__} has no dispatch for " - f"'{self._dispatch_lookup[left_visit_sym]}'" - ) - left_child = operator.attrgetter(left_attrname)(left) - right_child = operator.attrgetter(right_attrname)(right) - if left_child is None: - if right_child is not None: - return False - else: - continue - - comparison = dispatch( - left_attrname, left, left_child, right, right_child, **kw - ) - if comparison is COMPARE_FAILED: - return False - - return True - - def compare_inner(self, obj1, obj2, **kw): - comparator = self.__class__() - return comparator.compare(obj1, obj2, **kw) - - def visit_has_cache_key( - self, attrname, left_parent, left, right_parent, right, **kw - ): - if left._gen_cache_key(self.anon_map[0], []) != right._gen_cache_key( - self.anon_map[1], [] - ): - return COMPARE_FAILED - - def visit_propagate_attrs( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return self.compare_inner( - left.get("plugin_subject", None), right.get("plugin_subject", None) - ) - - def visit_has_cache_key_list( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for l, r in zip_longest(left, right, fillvalue=None): - if l is None: - if r is not None: - return COMPARE_FAILED - else: - continue - elif r is None: - return COMPARE_FAILED - - if l._gen_cache_key(self.anon_map[0], []) != r._gen_cache_key( - self.anon_map[1], [] - ): - return COMPARE_FAILED - - def visit_executable_options( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for l, r in zip_longest(left, right, fillvalue=None): - if l is None: - if r is not None: - return COMPARE_FAILED - else: - continue - elif r is None: - return COMPARE_FAILED - - if ( - l._gen_cache_key(self.anon_map[0], []) - if l._is_has_cache_key - else l - ) != ( - r._gen_cache_key(self.anon_map[1], []) - if r._is_has_cache_key - else r - ): - return COMPARE_FAILED - - def visit_clauseelement( - self, attrname, left_parent, left, right_parent, right, **kw - ): - self.stack.append((left, right)) - - def visit_fromclause_canonical_column_collection( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for lcol, rcol in zip_longest(left, right, fillvalue=None): - self.stack.append((lcol, rcol)) - - def visit_fromclause_derived_column_collection( - self, attrname, left_parent, left, right_parent, right, **kw - ): - pass - - def visit_string_clauseelement_dict( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for lstr, rstr in zip_longest( - sorted(left), sorted(right), fillvalue=None - ): - if lstr != rstr: - return COMPARE_FAILED - self.stack.append((left[lstr], right[rstr])) - - def visit_clauseelement_tuples( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for ltup, rtup in zip_longest(left, right, fillvalue=None): - if ltup is None or rtup is None: - return COMPARE_FAILED - - for l, r in zip_longest(ltup, rtup, fillvalue=None): - self.stack.append((l, r)) - - def visit_clauseelement_list( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for l, r in zip_longest(left, right, fillvalue=None): - self.stack.append((l, r)) - - def visit_clauseelement_tuple( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for l, r in zip_longest(left, right, fillvalue=None): - self.stack.append((l, r)) - - def _compare_unordered_sequences(self, seq1, seq2, **kw): - if seq1 is None: - return seq2 is None - - completed: Set[object] = set() - for clause in seq1: - for other_clause in set(seq2).difference(completed): - if self.compare_inner(clause, other_clause, **kw): - completed.add(other_clause) - break - return len(completed) == len(seq1) == len(seq2) - - def visit_clauseelement_unordered_set( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return self._compare_unordered_sequences(left, right, **kw) - - def visit_fromclause_ordered_set( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for l, r in zip_longest(left, right, fillvalue=None): - self.stack.append((l, r)) - - def visit_string( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_string_list( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_string_multi_dict( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for lk, rk in zip_longest( - sorted(left.keys()), sorted(right.keys()), fillvalue=(None, None) - ): - if lk != rk: - return COMPARE_FAILED - - lv, rv = left[lk], right[rk] - - lhc = isinstance(left, HasCacheKey) - rhc = isinstance(right, HasCacheKey) - if lhc and rhc: - if lv._gen_cache_key( - self.anon_map[0], [] - ) != rv._gen_cache_key(self.anon_map[1], []): - return COMPARE_FAILED - elif lhc != rhc: - return COMPARE_FAILED - elif lv != rv: - return COMPARE_FAILED - - def visit_multi( - self, attrname, left_parent, left, right_parent, right, **kw - ): - lhc = isinstance(left, HasCacheKey) - rhc = isinstance(right, HasCacheKey) - if lhc and rhc: - if left._gen_cache_key( - self.anon_map[0], [] - ) != right._gen_cache_key(self.anon_map[1], []): - return COMPARE_FAILED - elif lhc != rhc: - return COMPARE_FAILED - else: - return left == right - - def visit_anon_name( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return _resolve_name_for_compare( - left_parent, left, self.anon_map[0], **kw - ) == _resolve_name_for_compare( - right_parent, right, self.anon_map[1], **kw - ) - - def visit_boolean( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_operator( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_type( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left._compare_type_affinity(right) - - def visit_plain_dict( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_dialect_options( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_annotations_key( - self, attrname, left_parent, left, right_parent, right, **kw - ): - if left and right: - return ( - left_parent._annotations_cache_key - == right_parent._annotations_cache_key - ) - else: - return left == right - - def visit_with_context_options( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return tuple((fn.__code__, c_key) for fn, c_key in left) == tuple( - (fn.__code__, c_key) for fn, c_key in right - ) - - def visit_plain_obj( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_named_ddl_element( - self, attrname, left_parent, left, right_parent, right, **kw - ): - if left is None: - if right is not None: - return COMPARE_FAILED - - return left.name == right.name - - def visit_prefix_sequence( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for (l_clause, l_str), (r_clause, r_str) in zip_longest( - left, right, fillvalue=(None, None) - ): - if l_str != r_str: - return COMPARE_FAILED - else: - self.stack.append((l_clause, r_clause)) - - def visit_setup_join_tuple( - self, attrname, left_parent, left, right_parent, right, **kw - ): - # TODO: look at attrname for "legacy_join" and use different structure - for ( - (l_target, l_onclause, l_from, l_flags), - (r_target, r_onclause, r_from, r_flags), - ) in zip_longest(left, right, fillvalue=(None, None, None, None)): - if l_flags != r_flags: - return COMPARE_FAILED - self.stack.append((l_target, r_target)) - self.stack.append((l_onclause, r_onclause)) - self.stack.append((l_from, r_from)) - - def visit_memoized_select_entities( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return self.visit_clauseelement_tuple( - attrname, left_parent, left, right_parent, right, **kw - ) - - def visit_table_hint_list( - self, attrname, left_parent, left, right_parent, right, **kw - ): - left_keys = sorted(left, key=lambda elem: (elem[0].fullname, elem[1])) - right_keys = sorted( - right, key=lambda elem: (elem[0].fullname, elem[1]) - ) - for (ltable, ldialect), (rtable, rdialect) in zip_longest( - left_keys, right_keys, fillvalue=(None, None) - ): - if ldialect != rdialect: - return COMPARE_FAILED - elif left[(ltable, ldialect)] != right[(rtable, rdialect)]: - return COMPARE_FAILED - else: - self.stack.append((ltable, rtable)) - - def visit_statement_hint_list( - self, attrname, left_parent, left, right_parent, right, **kw - ): - return left == right - - def visit_unknown_structure( - self, attrname, left_parent, left, right_parent, right, **kw - ): - raise NotImplementedError() - - def visit_dml_ordered_values( - self, attrname, left_parent, left, right_parent, right, **kw - ): - # sequence of tuple pairs - - for (lk, lv), (rk, rv) in zip_longest( - left, right, fillvalue=(None, None) - ): - if not self._compare_dml_values_or_ce(lk, rk, **kw): - return COMPARE_FAILED - - def _compare_dml_values_or_ce(self, lv, rv, **kw): - lvce = hasattr(lv, "__clause_element__") - rvce = hasattr(rv, "__clause_element__") - if lvce != rvce: - return False - elif lvce and not self.compare_inner(lv, rv, **kw): - return False - elif not lvce and lv != rv: - return False - elif not self.compare_inner(lv, rv, **kw): - return False - - return True - - def visit_dml_values( - self, attrname, left_parent, left, right_parent, right, **kw - ): - if left is None or right is None or len(left) != len(right): - return COMPARE_FAILED - - if isinstance(left, collections_abc.Sequence): - for lv, rv in zip(left, right): - if not self._compare_dml_values_or_ce(lv, rv, **kw): - return COMPARE_FAILED - elif isinstance(right, collections_abc.Sequence): - return COMPARE_FAILED - else: - # dictionaries guaranteed to support insert ordering in - # py37 so that we can compare the keys in order. without - # this, we can't compare SQL expression keys because we don't - # know which key is which - for (lk, lv), (rk, rv) in zip(left.items(), right.items()): - if not self._compare_dml_values_or_ce(lk, rk, **kw): - return COMPARE_FAILED - if not self._compare_dml_values_or_ce(lv, rv, **kw): - return COMPARE_FAILED - - def visit_dml_multi_values( - self, attrname, left_parent, left, right_parent, right, **kw - ): - for lseq, rseq in zip_longest(left, right, fillvalue=None): - if lseq is None or rseq is None: - return COMPARE_FAILED - - for ld, rd in zip_longest(lseq, rseq, fillvalue=None): - if ( - self.visit_dml_values( - attrname, left_parent, ld, right_parent, rd, **kw - ) - is COMPARE_FAILED - ): - return COMPARE_FAILED - - def compare_expression_clauselist(self, left, right, **kw): - if left.operator is right.operator: - if operators.is_associative(left.operator): - if self._compare_unordered_sequences( - left.clauses, right.clauses, **kw - ): - return ["operator", "clauses"] - else: - return COMPARE_FAILED - else: - return ["operator"] - else: - return COMPARE_FAILED - - def compare_clauselist(self, left, right, **kw): - return self.compare_expression_clauselist(left, right, **kw) - - def compare_binary(self, left, right, **kw): - if left.operator == right.operator: - if operators.is_commutative(left.operator): - if ( - self.compare_inner(left.left, right.left, **kw) - and self.compare_inner(left.right, right.right, **kw) - ) or ( - self.compare_inner(left.left, right.right, **kw) - and self.compare_inner(left.right, right.left, **kw) - ): - return ["operator", "negate", "left", "right"] - else: - return COMPARE_FAILED - else: - return ["operator", "negate"] - else: - return COMPARE_FAILED - - def compare_bindparam(self, left, right, **kw): - compare_keys = kw.pop("compare_keys", True) - compare_values = kw.pop("compare_values", True) - - if compare_values: - omit = [] - else: - # this means, "skip these, we already compared" - omit = ["callable", "value"] - - if not compare_keys: - omit.append("key") - - return omit - - -class ColIdentityComparatorStrategy(TraversalComparatorStrategy): - def compare_column_element( - self, left, right, use_proxies=True, equivalents=(), **kw - ): - """Compare ColumnElements using proxies and equivalent collections. - - This is a comparison strategy specific to the ORM. - """ - - to_compare = (right,) - if equivalents and right in equivalents: - to_compare = equivalents[right].union(to_compare) - - for oth in to_compare: - if use_proxies and left.shares_lineage(oth): - return SKIP_TRAVERSE - elif hash(left) == hash(right): - return SKIP_TRAVERSE - else: - return COMPARE_FAILED - - def compare_column(self, left, right, **kw): - return self.compare_column_element(left, right, **kw) - - def compare_label(self, left, right, **kw): - return self.compare_column_element(left, right, **kw) - - def compare_table(self, left, right, **kw): - # tables compare on identity, since it's not really feasible to - # compare them column by column with the above rules - return SKIP_TRAVERSE if left is right else COMPARE_FAILED diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/type_api.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/type_api.py deleted file mode 100644 index 4233e7f..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/type_api.py +++ /dev/null @@ -1,2303 +0,0 @@ -# sql/type_api.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""Base types API. - -""" - -from __future__ import annotations - -from enum import Enum -from types import ModuleType -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Generic -from typing import Mapping -from typing import NewType -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 typing import Union - -from .base import SchemaEventTarget -from .cache_key import CacheConst -from .cache_key import NO_CACHE -from .operators import ColumnOperators -from .visitors import Visitable -from .. import exc -from .. import util -from ..util.typing import Protocol -from ..util.typing import Self -from ..util.typing import TypeAliasType -from ..util.typing import TypedDict -from ..util.typing import TypeGuard - -# these are back-assigned by sqltypes. -if typing.TYPE_CHECKING: - from ._typing import _TypeEngineArgument - from .elements import BindParameter - from .elements import ColumnElement - from .operators import OperatorType - from .sqltypes import _resolve_value_to_type as _resolve_value_to_type - from .sqltypes import BOOLEANTYPE as BOOLEANTYPE # noqa: F401 - from .sqltypes import INDEXABLE as INDEXABLE # noqa: F401 - from .sqltypes import INTEGERTYPE as INTEGERTYPE # noqa: F401 - from .sqltypes import MATCHTYPE as MATCHTYPE # noqa: F401 - from .sqltypes import NULLTYPE as NULLTYPE - from .sqltypes import NUMERICTYPE as NUMERICTYPE # noqa: F401 - from .sqltypes import STRINGTYPE as STRINGTYPE # noqa: F401 - from .sqltypes import TABLEVALUE as TABLEVALUE # noqa: F401 - from ..engine.interfaces import Dialect - from ..util.typing import GenericProtocol - -_T = TypeVar("_T", bound=Any) -_T_co = TypeVar("_T_co", bound=Any, covariant=True) -_T_con = TypeVar("_T_con", bound=Any, contravariant=True) -_O = TypeVar("_O", bound=object) -_TE = TypeVar("_TE", bound="TypeEngine[Any]") -_CT = TypeVar("_CT", bound=Any) - -_MatchedOnType = Union[ - "GenericProtocol[Any]", TypeAliasType, NewType, Type[Any] -] - - -class _NoValueInList(Enum): - NO_VALUE_IN_LIST = 0 - """indicates we are trying to determine the type of an expression - against an empty list.""" - - -_NO_VALUE_IN_LIST = _NoValueInList.NO_VALUE_IN_LIST - - -class _LiteralProcessorType(Protocol[_T_co]): - def __call__(self, value: Any) -> str: ... - - -class _BindProcessorType(Protocol[_T_con]): - def __call__(self, value: Optional[_T_con]) -> Any: ... - - -class _ResultProcessorType(Protocol[_T_co]): - def __call__(self, value: Any) -> Optional[_T_co]: ... - - -class _SentinelProcessorType(Protocol[_T_co]): - def __call__(self, value: Any) -> Optional[_T_co]: ... - - -class _BaseTypeMemoDict(TypedDict): - impl: TypeEngine[Any] - result: Dict[Any, Optional[_ResultProcessorType[Any]]] - - -class _TypeMemoDict(_BaseTypeMemoDict, total=False): - literal: Optional[_LiteralProcessorType[Any]] - bind: Optional[_BindProcessorType[Any]] - sentinel: Optional[_SentinelProcessorType[Any]] - custom: Dict[Any, object] - - -class _ComparatorFactory(Protocol[_T]): - def __call__( - self, expr: ColumnElement[_T] - ) -> TypeEngine.Comparator[_T]: ... - - -class TypeEngine(Visitable, Generic[_T]): - """The ultimate base class for all SQL datatypes. - - Common subclasses of :class:`.TypeEngine` include - :class:`.String`, :class:`.Integer`, and :class:`.Boolean`. - - For an overview of the SQLAlchemy typing system, see - :ref:`types_toplevel`. - - .. seealso:: - - :ref:`types_toplevel` - - """ - - _sqla_type = True - _isnull = False - _is_tuple_type = False - _is_table_value = False - _is_array = False - _is_type_decorator = False - - render_bind_cast = False - """Render bind casts for :attr:`.BindTyping.RENDER_CASTS` mode. - - If True, this type (usually a dialect level impl type) signals - to the compiler that a cast should be rendered around a bound parameter - for this type. - - .. versionadded:: 2.0 - - .. seealso:: - - :class:`.BindTyping` - - """ - - render_literal_cast = False - """render casts when rendering a value as an inline literal, - e.g. with :meth:`.TypeEngine.literal_processor`. - - .. versionadded:: 2.0 - - """ - - class Comparator( - ColumnOperators, - Generic[_CT], - ): - """Base class for custom comparison operations defined at the - type level. See :attr:`.TypeEngine.comparator_factory`. - - - """ - - __slots__ = "expr", "type" - - expr: ColumnElement[_CT] - type: TypeEngine[_CT] - - def __clause_element__(self) -> ColumnElement[_CT]: - return self.expr - - def __init__(self, expr: ColumnElement[_CT]): - self.expr = expr - self.type = expr.type - - @util.preload_module("sqlalchemy.sql.default_comparator") - def operate( - self, op: OperatorType, *other: Any, **kwargs: Any - ) -> ColumnElement[_CT]: - default_comparator = util.preloaded.sql_default_comparator - op_fn, addtl_kw = default_comparator.operator_lookup[op.__name__] - if kwargs: - addtl_kw = addtl_kw.union(kwargs) - return op_fn(self.expr, op, *other, **addtl_kw) - - @util.preload_module("sqlalchemy.sql.default_comparator") - def reverse_operate( - self, op: OperatorType, other: Any, **kwargs: Any - ) -> ColumnElement[_CT]: - default_comparator = util.preloaded.sql_default_comparator - op_fn, addtl_kw = default_comparator.operator_lookup[op.__name__] - if kwargs: - addtl_kw = addtl_kw.union(kwargs) - return op_fn(self.expr, op, other, reverse=True, **addtl_kw) - - def _adapt_expression( - self, - op: OperatorType, - other_comparator: TypeEngine.Comparator[Any], - ) -> Tuple[OperatorType, TypeEngine[Any]]: - """evaluate the return type of <self> <op> <othertype>, - and apply any adaptations to the given operator. - - This method determines the type of a resulting binary expression - given two source types and an operator. For example, two - :class:`_schema.Column` objects, both of the type - :class:`.Integer`, will - produce a :class:`.BinaryExpression` that also has the type - :class:`.Integer` when compared via the addition (``+``) operator. - However, using the addition operator with an :class:`.Integer` - and a :class:`.Date` object will produce a :class:`.Date`, assuming - "days delta" behavior by the database (in reality, most databases - other than PostgreSQL don't accept this particular operation). - - The method returns a tuple of the form <operator>, <type>. - The resulting operator and type will be those applied to the - resulting :class:`.BinaryExpression` as the final operator and the - right-hand side of the expression. - - Note that only a subset of operators make usage of - :meth:`._adapt_expression`, - including math operators and user-defined operators, but not - boolean comparison or special SQL keywords like MATCH or BETWEEN. - - """ - - return op, self.type - - hashable = True - """Flag, if False, means values from this type aren't hashable. - - Used by the ORM when uniquing result lists. - - """ - - comparator_factory: _ComparatorFactory[Any] = Comparator - """A :class:`.TypeEngine.Comparator` class which will apply - to operations performed by owning :class:`_expression.ColumnElement` - objects. - - The :attr:`.comparator_factory` attribute is a hook consulted by - the core expression system when column and SQL expression operations - are performed. When a :class:`.TypeEngine.Comparator` class is - associated with this attribute, it allows custom re-definition of - all existing operators, as well as definition of new operators. - Existing operators include those provided by Python operator overloading - such as :meth:`.operators.ColumnOperators.__add__` and - :meth:`.operators.ColumnOperators.__eq__`, - those provided as standard - attributes of :class:`.operators.ColumnOperators` such as - :meth:`.operators.ColumnOperators.like` - and :meth:`.operators.ColumnOperators.in_`. - - Rudimentary usage of this hook is allowed through simple subclassing - of existing types, or alternatively by using :class:`.TypeDecorator`. - See the documentation section :ref:`types_operators` for examples. - - """ - - sort_key_function: Optional[Callable[[Any], Any]] = None - """A sorting function that can be passed as the key to sorted. - - The default value of ``None`` indicates that the values stored by - this type are self-sorting. - - .. versionadded:: 1.3.8 - - """ - - should_evaluate_none: bool = False - """If True, the Python constant ``None`` is considered to be handled - explicitly by this type. - - The ORM uses this flag to indicate that a positive value of ``None`` - is passed to the column in an INSERT statement, rather than omitting - the column from the INSERT statement which has the effect of firing - off column-level defaults. It also allows types which have special - behavior for Python None, such as a JSON type, to indicate that - they'd like to handle the None value explicitly. - - To set this flag on an existing type, use the - :meth:`.TypeEngine.evaluates_none` method. - - .. seealso:: - - :meth:`.TypeEngine.evaluates_none` - - """ - - _variant_mapping: util.immutabledict[str, TypeEngine[Any]] = ( - util.EMPTY_DICT - ) - - def evaluates_none(self) -> Self: - """Return a copy of this type which has the - :attr:`.should_evaluate_none` flag set to True. - - E.g.:: - - Table( - 'some_table', metadata, - Column( - String(50).evaluates_none(), - nullable=True, - server_default='no value') - ) - - The ORM uses this flag to indicate that a positive value of ``None`` - is passed to the column in an INSERT statement, rather than omitting - the column from the INSERT statement which has the effect of firing - off column-level defaults. It also allows for types which have - special behavior associated with the Python None value to indicate - that the value doesn't necessarily translate into SQL NULL; a - prime example of this is a JSON type which may wish to persist the - JSON value ``'null'``. - - In all cases, the actual NULL SQL value can be always be - persisted in any column by using - the :obj:`_expression.null` SQL construct in an INSERT statement - or associated with an ORM-mapped attribute. - - .. note:: - - The "evaluates none" flag does **not** apply to a value - of ``None`` passed to :paramref:`_schema.Column.default` or - :paramref:`_schema.Column.server_default`; in these cases, - ``None`` - still means "no default". - - .. seealso:: - - :ref:`session_forcing_null` - in the ORM documentation - - :paramref:`.postgresql.JSON.none_as_null` - PostgreSQL JSON - interaction with this flag. - - :attr:`.TypeEngine.should_evaluate_none` - class-level flag - - """ - typ = self.copy() - typ.should_evaluate_none = True - return typ - - def copy(self, **kw: Any) -> Self: - return self.adapt(self.__class__) - - def copy_value(self, value: Any) -> Any: - return value - - def literal_processor( - self, dialect: Dialect - ) -> Optional[_LiteralProcessorType[_T]]: - """Return a conversion function for processing literal values that are - to be rendered directly without using binds. - - This function is used when the compiler makes use of the - "literal_binds" flag, typically used in DDL generation as well - as in certain scenarios where backends don't accept bound parameters. - - Returns a callable which will receive a literal Python value - as the sole positional argument and will return a string representation - to be rendered in a SQL statement. - - .. note:: - - This method is only called relative to a **dialect specific type - object**, which is often **private to a dialect in use** and is not - the same type object as the public facing one, which means it's not - feasible to subclass a :class:`.types.TypeEngine` class in order to - provide an alternate :meth:`_types.TypeEngine.literal_processor` - method, unless subclassing the :class:`_types.UserDefinedType` - class explicitly. - - To provide alternate behavior for - :meth:`_types.TypeEngine.literal_processor`, implement a - :class:`_types.TypeDecorator` class and provide an implementation - of :meth:`_types.TypeDecorator.process_literal_param`. - - .. seealso:: - - :ref:`types_typedecorator` - - - """ - return None - - def bind_processor( - self, dialect: Dialect - ) -> Optional[_BindProcessorType[_T]]: - """Return a conversion function for processing bind values. - - Returns a callable which will receive a bind parameter value - as the sole positional argument and will return a value to - send to the DB-API. - - If processing is not necessary, the method should return ``None``. - - .. note:: - - This method is only called relative to a **dialect specific type - object**, which is often **private to a dialect in use** and is not - the same type object as the public facing one, which means it's not - feasible to subclass a :class:`.types.TypeEngine` class in order to - provide an alternate :meth:`_types.TypeEngine.bind_processor` - method, unless subclassing the :class:`_types.UserDefinedType` - class explicitly. - - To provide alternate behavior for - :meth:`_types.TypeEngine.bind_processor`, implement a - :class:`_types.TypeDecorator` class and provide an implementation - of :meth:`_types.TypeDecorator.process_bind_param`. - - .. seealso:: - - :ref:`types_typedecorator` - - - :param dialect: Dialect instance in use. - - """ - return None - - def result_processor( - self, dialect: Dialect, coltype: object - ) -> Optional[_ResultProcessorType[_T]]: - """Return a conversion function for processing result row values. - - Returns a callable which will receive a result row column - value as the sole positional argument and will return a value - to return to the user. - - If processing is not necessary, the method should return ``None``. - - .. note:: - - This method is only called relative to a **dialect specific type - object**, which is often **private to a dialect in use** and is not - the same type object as the public facing one, which means it's not - feasible to subclass a :class:`.types.TypeEngine` class in order to - provide an alternate :meth:`_types.TypeEngine.result_processor` - method, unless subclassing the :class:`_types.UserDefinedType` - class explicitly. - - To provide alternate behavior for - :meth:`_types.TypeEngine.result_processor`, implement a - :class:`_types.TypeDecorator` class and provide an implementation - of :meth:`_types.TypeDecorator.process_result_value`. - - .. seealso:: - - :ref:`types_typedecorator` - - :param dialect: Dialect instance in use. - - :param coltype: DBAPI coltype argument received in cursor.description. - - """ - return None - - def column_expression( - self, colexpr: ColumnElement[_T] - ) -> Optional[ColumnElement[_T]]: - """Given a SELECT column expression, return a wrapping SQL expression. - - This is typically a SQL function that wraps a column expression - as rendered in the columns clause of a SELECT statement. - It is used for special data types that require - columns to be wrapped in some special database function in order - to coerce the value before being sent back to the application. - It is the SQL analogue of the :meth:`.TypeEngine.result_processor` - method. - - This method is called during the **SQL compilation** phase of a - statement, when rendering a SQL string. It is **not** called - against specific values. - - .. note:: - - This method is only called relative to a **dialect specific type - object**, which is often **private to a dialect in use** and is not - the same type object as the public facing one, which means it's not - feasible to subclass a :class:`.types.TypeEngine` class in order to - provide an alternate :meth:`_types.TypeEngine.column_expression` - method, unless subclassing the :class:`_types.UserDefinedType` - class explicitly. - - To provide alternate behavior for - :meth:`_types.TypeEngine.column_expression`, implement a - :class:`_types.TypeDecorator` class and provide an implementation - of :meth:`_types.TypeDecorator.column_expression`. - - .. seealso:: - - :ref:`types_typedecorator` - - - .. seealso:: - - :ref:`types_sql_value_processing` - - """ - - return None - - @util.memoized_property - def _has_column_expression(self) -> bool: - """memoized boolean, check if column_expression is implemented. - - Allows the method to be skipped for the vast majority of expression - types that don't use this feature. - - """ - - return ( - self.__class__.column_expression.__code__ - is not TypeEngine.column_expression.__code__ - ) - - def bind_expression( - self, bindvalue: BindParameter[_T] - ) -> Optional[ColumnElement[_T]]: - """Given a bind value (i.e. a :class:`.BindParameter` instance), - return a SQL expression in its place. - - This is typically a SQL function that wraps the existing bound - parameter within the statement. It is used for special data types - that require literals being wrapped in some special database function - in order to coerce an application-level value into a database-specific - format. It is the SQL analogue of the - :meth:`.TypeEngine.bind_processor` method. - - This method is called during the **SQL compilation** phase of a - statement, when rendering a SQL string. It is **not** called - against specific values. - - Note that this method, when implemented, should always return - the exact same structure, without any conditional logic, as it - may be used in an executemany() call against an arbitrary number - of bound parameter sets. - - .. note:: - - This method is only called relative to a **dialect specific type - object**, which is often **private to a dialect in use** and is not - the same type object as the public facing one, which means it's not - feasible to subclass a :class:`.types.TypeEngine` class in order to - provide an alternate :meth:`_types.TypeEngine.bind_expression` - method, unless subclassing the :class:`_types.UserDefinedType` - class explicitly. - - To provide alternate behavior for - :meth:`_types.TypeEngine.bind_expression`, implement a - :class:`_types.TypeDecorator` class and provide an implementation - of :meth:`_types.TypeDecorator.bind_expression`. - - .. seealso:: - - :ref:`types_typedecorator` - - .. seealso:: - - :ref:`types_sql_value_processing` - - """ - return None - - @util.memoized_property - def _has_bind_expression(self) -> bool: - """memoized boolean, check if bind_expression is implemented. - - Allows the method to be skipped for the vast majority of expression - types that don't use this feature. - - """ - - return util.method_is_overridden(self, TypeEngine.bind_expression) - - @staticmethod - def _to_instance(cls_or_self: Union[Type[_TE], _TE]) -> _TE: - return to_instance(cls_or_self) - - def compare_values(self, x: Any, y: Any) -> bool: - """Compare two values for equality.""" - - return x == y # type: ignore[no-any-return] - - def get_dbapi_type(self, dbapi: ModuleType) -> Optional[Any]: - """Return the corresponding type object from the underlying DB-API, if - any. - - This can be useful for calling ``setinputsizes()``, for example. - - """ - return None - - @property - def python_type(self) -> Type[Any]: - """Return the Python type object expected to be returned - by instances of this type, if known. - - Basically, for those types which enforce a return type, - or are known across the board to do such for all common - DBAPIs (like ``int`` for example), will return that type. - - If a return type is not defined, raises - ``NotImplementedError``. - - Note that any type also accommodates NULL in SQL which - means you can also get back ``None`` from any type - in practice. - - """ - raise NotImplementedError() - - def with_variant( - self, - type_: _TypeEngineArgument[Any], - *dialect_names: str, - ) -> Self: - r"""Produce a copy of this type object that will utilize the given - type when applied to the dialect of the given name. - - e.g.:: - - from sqlalchemy.types import String - from sqlalchemy.dialects import mysql - - string_type = String() - - string_type = string_type.with_variant( - mysql.VARCHAR(collation='foo'), 'mysql', 'mariadb' - ) - - The variant mapping indicates that when this type is - interpreted by a specific dialect, it will instead be - transmuted into the given type, rather than using the - primary type. - - .. versionchanged:: 2.0 the :meth:`_types.TypeEngine.with_variant` - method now works with a :class:`_types.TypeEngine` object "in - place", returning a copy of the original type rather than returning - a wrapping object; the ``Variant`` class is no longer used. - - :param type\_: a :class:`.TypeEngine` that will be selected - as a variant from the originating type, when a dialect - of the given name is in use. - :param \*dialect_names: one or more base names of the dialect which - uses this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.) - - .. versionchanged:: 2.0 multiple dialect names can be specified - for one variant. - - .. seealso:: - - :ref:`types_with_variant` - illustrates the use of - :meth:`_types.TypeEngine.with_variant`. - - """ - - if not dialect_names: - raise exc.ArgumentError("At least one dialect name is required") - for dialect_name in dialect_names: - if dialect_name in self._variant_mapping: - raise exc.ArgumentError( - f"Dialect {dialect_name!r} is already present in " - f"the mapping for this {self!r}" - ) - new_type = self.copy() - type_ = to_instance(type_) - if type_._variant_mapping: - raise exc.ArgumentError( - "can't pass a type that already has variants as a " - "dialect-level type to with_variant()" - ) - - new_type._variant_mapping = self._variant_mapping.union( - {dialect_name: type_ for dialect_name in dialect_names} - ) - return new_type - - def _resolve_for_literal(self, value: Any) -> Self: - """adjust this type given a literal Python value that will be - stored in a bound parameter. - - Used exclusively by _resolve_value_to_type(). - - .. versionadded:: 1.4.30 or 2.0 - - TODO: this should be part of public API - - .. seealso:: - - :meth:`.TypeEngine._resolve_for_python_type` - - """ - return self - - def _resolve_for_python_type( - self, - python_type: Type[Any], - matched_on: _MatchedOnType, - matched_on_flattened: Type[Any], - ) -> Optional[Self]: - """given a Python type (e.g. ``int``, ``str``, etc. ) return an - instance of this :class:`.TypeEngine` that's appropriate for this type. - - An additional argument ``matched_on`` is passed, which indicates an - entry from the ``__mro__`` of the given ``python_type`` that more - specifically matches how the caller located this :class:`.TypeEngine` - object. Such as, if a lookup of some kind links the ``int`` Python - type to the :class:`.Integer` SQL type, and the original object - was some custom subclass of ``int`` such as ``MyInt(int)``, the - arguments passed would be ``(MyInt, int)``. - - If the given Python type does not correspond to this - :class:`.TypeEngine`, or the Python type is otherwise ambiguous, the - method should return None. - - For simple cases, the method checks that the ``python_type`` - and ``matched_on`` types are the same (i.e. not a subclass), and - returns self; for all other cases, it returns ``None``. - - The initial use case here is for the ORM to link user-defined - Python standard library ``enum.Enum`` classes to the SQLAlchemy - :class:`.Enum` SQL type when constructing ORM Declarative mappings. - - :param python_type: the Python type we want to use - :param matched_on: the Python type that led us to choose this - particular :class:`.TypeEngine` class, which would be a supertype - of ``python_type``. By default, the request is rejected if - ``python_type`` doesn't match ``matched_on`` (None is returned). - - .. versionadded:: 2.0.0b4 - - TODO: this should be part of public API - - .. seealso:: - - :meth:`.TypeEngine._resolve_for_literal` - - """ - - if python_type is not matched_on_flattened: - return None - - return self - - @util.ro_memoized_property - def _type_affinity(self) -> Optional[Type[TypeEngine[_T]]]: - """Return a rudimental 'affinity' value expressing the general class - of type.""" - - typ = None - for t in self.__class__.__mro__: - if t is TypeEngine or TypeEngineMixin in t.__bases__: - return typ - elif issubclass(t, TypeEngine): - typ = t - else: - return self.__class__ - - @util.ro_memoized_property - def _generic_type_affinity( - self, - ) -> Type[TypeEngine[_T]]: - best_camelcase = None - best_uppercase = None - - if not isinstance(self, TypeEngine): - return self.__class__ - - for t in self.__class__.__mro__: - if ( - t.__module__ - in ( - "sqlalchemy.sql.sqltypes", - "sqlalchemy.sql.type_api", - ) - and issubclass(t, TypeEngine) - and TypeEngineMixin not in t.__bases__ - and t not in (TypeEngine, TypeEngineMixin) - and t.__name__[0] != "_" - ): - if t.__name__.isupper() and not best_uppercase: - best_uppercase = t - elif not t.__name__.isupper() and not best_camelcase: - best_camelcase = t - - return ( - best_camelcase - or best_uppercase - or cast("Type[TypeEngine[_T]]", NULLTYPE.__class__) - ) - - def as_generic(self, allow_nulltype: bool = False) -> TypeEngine[_T]: - """ - Return an instance of the generic type corresponding to this type - using heuristic rule. The method may be overridden if this - heuristic rule is not sufficient. - - >>> from sqlalchemy.dialects.mysql import INTEGER - >>> INTEGER(display_width=4).as_generic() - Integer() - - >>> from sqlalchemy.dialects.mysql import NVARCHAR - >>> NVARCHAR(length=100).as_generic() - Unicode(length=100) - - .. versionadded:: 1.4.0b2 - - - .. seealso:: - - :ref:`metadata_reflection_dbagnostic_types` - describes the - use of :meth:`_types.TypeEngine.as_generic` in conjunction with - the :meth:`_sql.DDLEvents.column_reflect` event, which is its - intended use. - - """ - if ( - not allow_nulltype - and self._generic_type_affinity == NULLTYPE.__class__ - ): - raise NotImplementedError( - "Default TypeEngine.as_generic() " - "heuristic method was unsuccessful for {}. A custom " - "as_generic() method must be implemented for this " - "type class.".format( - self.__class__.__module__ + "." + self.__class__.__name__ - ) - ) - - return util.constructor_copy(self, self._generic_type_affinity) - - def dialect_impl(self, dialect: Dialect) -> TypeEngine[_T]: - """Return a dialect-specific implementation for this - :class:`.TypeEngine`. - - """ - try: - tm = dialect._type_memos[self] - except KeyError: - pass - else: - return tm["impl"] - return self._dialect_info(dialect)["impl"] - - def _unwrapped_dialect_impl(self, dialect: Dialect) -> TypeEngine[_T]: - """Return the 'unwrapped' dialect impl for this type. - - For a type that applies wrapping logic (e.g. TypeDecorator), give - us the real, actual dialect-level type that is used. - - This is used by TypeDecorator itself as well at least one case where - dialects need to check that a particular specific dialect-level - type is in use, within the :meth:`.DefaultDialect.set_input_sizes` - method. - - """ - return self.dialect_impl(dialect) - - def _cached_literal_processor( - self, dialect: Dialect - ) -> Optional[_LiteralProcessorType[_T]]: - """Return a dialect-specific literal processor for this type.""" - - try: - return dialect._type_memos[self]["literal"] - except KeyError: - pass - - # avoid KeyError context coming into literal_processor() function - # raises - d = self._dialect_info(dialect) - d["literal"] = lp = d["impl"].literal_processor(dialect) - return lp - - def _cached_bind_processor( - self, dialect: Dialect - ) -> Optional[_BindProcessorType[_T]]: - """Return a dialect-specific bind processor for this type.""" - - try: - return dialect._type_memos[self]["bind"] - except KeyError: - pass - - # avoid KeyError context coming into bind_processor() function - # raises - d = self._dialect_info(dialect) - d["bind"] = bp = d["impl"].bind_processor(dialect) - return bp - - def _cached_result_processor( - self, dialect: Dialect, coltype: Any - ) -> Optional[_ResultProcessorType[_T]]: - """Return a dialect-specific result processor for this type.""" - - try: - return dialect._type_memos[self]["result"][coltype] - except KeyError: - pass - - # avoid KeyError context coming into result_processor() function - # raises - d = self._dialect_info(dialect) - # key assumption: DBAPI type codes are - # constants. Else this dictionary would - # grow unbounded. - rp = d["impl"].result_processor(dialect, coltype) - d["result"][coltype] = rp - return rp - - def _cached_custom_processor( - self, dialect: Dialect, key: str, fn: Callable[[TypeEngine[_T]], _O] - ) -> _O: - """return a dialect-specific processing object for - custom purposes. - - The cx_Oracle dialect uses this at the moment. - - """ - try: - return cast(_O, dialect._type_memos[self]["custom"][key]) - except KeyError: - pass - # avoid KeyError context coming into fn() function - # raises - d = self._dialect_info(dialect) - impl = d["impl"] - custom_dict = d.setdefault("custom", {}) - custom_dict[key] = result = fn(impl) - return result - - def _dialect_info(self, dialect: Dialect) -> _TypeMemoDict: - """Return a dialect-specific registry which - caches a dialect-specific implementation, bind processing - function, and one or more result processing functions.""" - - if self in dialect._type_memos: - return dialect._type_memos[self] - else: - impl = self._gen_dialect_impl(dialect) - if impl is self: - impl = self.adapt(type(self)) - # this can't be self, else we create a cycle - assert impl is not self - d: _TypeMemoDict = {"impl": impl, "result": {}} - dialect._type_memos[self] = d - return d - - def _gen_dialect_impl(self, dialect: Dialect) -> TypeEngine[Any]: - if dialect.name in self._variant_mapping: - return self._variant_mapping[dialect.name]._gen_dialect_impl( - dialect - ) - else: - return dialect.type_descriptor(self) - - @util.memoized_property - def _static_cache_key( - self, - ) -> Union[CacheConst, Tuple[Any, ...]]: - names = util.get_cls_kwargs(self.__class__) - return (self.__class__,) + tuple( - ( - k, - ( - self.__dict__[k]._static_cache_key - if isinstance(self.__dict__[k], TypeEngine) - else self.__dict__[k] - ), - ) - for k in names - if k in self.__dict__ - and not k.startswith("_") - and self.__dict__[k] is not None - ) - - @overload - def adapt(self, cls: Type[_TE], **kw: Any) -> _TE: ... - - @overload - def adapt( - self, cls: Type[TypeEngineMixin], **kw: Any - ) -> TypeEngine[Any]: ... - - def adapt( - self, cls: Type[Union[TypeEngine[Any], TypeEngineMixin]], **kw: Any - ) -> TypeEngine[Any]: - """Produce an "adapted" form of this type, given an "impl" class - to work with. - - This method is used internally to associate generic - types with "implementation" types that are specific to a particular - dialect. - """ - typ = util.constructor_copy( - self, cast(Type[TypeEngine[Any]], cls), **kw - ) - typ._variant_mapping = self._variant_mapping - return typ - - def coerce_compared_value( - self, op: Optional[OperatorType], value: Any - ) -> TypeEngine[Any]: - """Suggest a type for a 'coerced' Python value in an expression. - - Given an operator and value, gives the type a chance - to return a type which the value should be coerced into. - - The default behavior here is conservative; if the right-hand - side is already coerced into a SQL type based on its - Python type, it is usually left alone. - - End-user functionality extension here should generally be via - :class:`.TypeDecorator`, which provides more liberal behavior in that - it defaults to coercing the other side of the expression into this - type, thus applying special Python conversions above and beyond those - needed by the DBAPI to both ides. It also provides the public method - :meth:`.TypeDecorator.coerce_compared_value` which is intended for - end-user customization of this behavior. - - """ - _coerced_type = _resolve_value_to_type(value) - if ( - _coerced_type is NULLTYPE - or _coerced_type._type_affinity is self._type_affinity - ): - return self - else: - return _coerced_type - - def _compare_type_affinity(self, other: TypeEngine[Any]) -> bool: - return self._type_affinity is other._type_affinity - - def compile(self, dialect: Optional[Dialect] = None) -> str: - """Produce a string-compiled form of this :class:`.TypeEngine`. - - When called with no arguments, uses a "default" dialect - to produce a string result. - - :param dialect: a :class:`.Dialect` instance. - - """ - # arg, return value is inconsistent with - # ClauseElement.compile()....this is a mistake. - - if dialect is None: - dialect = self._default_dialect() - - return dialect.type_compiler_instance.process(self) - - @util.preload_module("sqlalchemy.engine.default") - def _default_dialect(self) -> Dialect: - default = util.preloaded.engine_default - - # dmypy / mypy seems to sporadically keep thinking this line is - # returning Any, which seems to be caused by the @deprecated_params - # decorator on the DefaultDialect constructor - return default.StrCompileDialect() # type: ignore - - def __str__(self) -> str: - return str(self.compile()) - - def __repr__(self) -> str: - return util.generic_repr(self) - - -class TypeEngineMixin: - """classes which subclass this can act as "mixin" classes for - TypeEngine.""" - - __slots__ = () - - if TYPE_CHECKING: - - @util.memoized_property - def _static_cache_key( - self, - ) -> Union[CacheConst, Tuple[Any, ...]]: ... - - @overload - def adapt(self, cls: Type[_TE], **kw: Any) -> _TE: ... - - @overload - def adapt( - self, cls: Type[TypeEngineMixin], **kw: Any - ) -> TypeEngine[Any]: ... - - def adapt( - self, cls: Type[Union[TypeEngine[Any], TypeEngineMixin]], **kw: Any - ) -> TypeEngine[Any]: ... - - def dialect_impl(self, dialect: Dialect) -> TypeEngine[Any]: ... - - -class ExternalType(TypeEngineMixin): - """mixin that defines attributes and behaviors specific to third-party - datatypes. - - "Third party" refers to datatypes that are defined outside the scope - of SQLAlchemy within either end-user application code or within - external extensions to SQLAlchemy. - - Subclasses currently include :class:`.TypeDecorator` and - :class:`.UserDefinedType`. - - .. versionadded:: 1.4.28 - - """ - - cache_ok: Optional[bool] = None - """Indicate if statements using this :class:`.ExternalType` are "safe to - cache". - - The default value ``None`` will emit a warning and then not allow caching - of a statement which includes this type. Set to ``False`` to disable - statements using this type from being cached at all without a warning. - When set to ``True``, the object's class and selected elements from its - state will be used as part of the cache key. For example, using a - :class:`.TypeDecorator`:: - - class MyType(TypeDecorator): - impl = String - - cache_ok = True - - def __init__(self, choices): - self.choices = tuple(choices) - self.internal_only = True - - The cache key for the above type would be equivalent to:: - - >>> MyType(["a", "b", "c"])._static_cache_key - (<class '__main__.MyType'>, ('choices', ('a', 'b', 'c'))) - - The caching scheme will extract attributes from the type that correspond - to the names of parameters in the ``__init__()`` method. Above, the - "choices" attribute becomes part of the cache key but "internal_only" - does not, because there is no parameter named "internal_only". - - The requirements for cacheable elements is that they are hashable - and also that they indicate the same SQL rendered for expressions using - this type every time for a given cache value. - - To accommodate for datatypes that refer to unhashable structures such - as dictionaries, sets and lists, these objects can be made "cacheable" - by assigning hashable structures to the attributes whose names - correspond with the names of the arguments. For example, a datatype - which accepts a dictionary of lookup values may publish this as a sorted - series of tuples. Given a previously un-cacheable type as:: - - class LookupType(UserDefinedType): - '''a custom type that accepts a dictionary as a parameter. - - this is the non-cacheable version, as "self.lookup" is not - hashable. - - ''' - - def __init__(self, lookup): - self.lookup = lookup - - def get_col_spec(self, **kw): - return "VARCHAR(255)" - - def bind_processor(self, dialect): - # ... works with "self.lookup" ... - - Where "lookup" is a dictionary. The type will not be able to generate - a cache key:: - - >>> type_ = LookupType({"a": 10, "b": 20}) - >>> type_._static_cache_key - <stdin>:1: SAWarning: UserDefinedType LookupType({'a': 10, 'b': 20}) will not - produce a cache key because the ``cache_ok`` flag is not set to True. - Set this flag to True if this type object's state is safe to use - in a cache key, or False to disable this warning. - symbol('no_cache') - - If we **did** set up such a cache key, it wouldn't be usable. We would - get a tuple structure that contains a dictionary inside of it, which - cannot itself be used as a key in a "cache dictionary" such as SQLAlchemy's - statement cache, since Python dictionaries aren't hashable:: - - >>> # set cache_ok = True - >>> type_.cache_ok = True - - >>> # this is the cache key it would generate - >>> key = type_._static_cache_key - >>> key - (<class '__main__.LookupType'>, ('lookup', {'a': 10, 'b': 20})) - - >>> # however this key is not hashable, will fail when used with - >>> # SQLAlchemy statement cache - >>> some_cache = {key: "some sql value"} - Traceback (most recent call last): File "<stdin>", line 1, - in <module> TypeError: unhashable type: 'dict' - - The type may be made cacheable by assigning a sorted tuple of tuples - to the ".lookup" attribute:: - - class LookupType(UserDefinedType): - '''a custom type that accepts a dictionary as a parameter. - - The dictionary is stored both as itself in a private variable, - and published in a public variable as a sorted tuple of tuples, - which is hashable and will also return the same value for any - two equivalent dictionaries. Note it assumes the keys and - values of the dictionary are themselves hashable. - - ''' - - cache_ok = True - - def __init__(self, lookup): - self._lookup = lookup - - # assume keys/values of "lookup" are hashable; otherwise - # they would also need to be converted in some way here - self.lookup = tuple( - (key, lookup[key]) for key in sorted(lookup) - ) - - def get_col_spec(self, **kw): - return "VARCHAR(255)" - - def bind_processor(self, dialect): - # ... works with "self._lookup" ... - - Where above, the cache key for ``LookupType({"a": 10, "b": 20})`` will be:: - - >>> LookupType({"a": 10, "b": 20})._static_cache_key - (<class '__main__.LookupType'>, ('lookup', (('a', 10), ('b', 20)))) - - .. versionadded:: 1.4.14 - added the ``cache_ok`` flag to allow - some configurability of caching for :class:`.TypeDecorator` classes. - - .. versionadded:: 1.4.28 - added the :class:`.ExternalType` mixin which - generalizes the ``cache_ok`` flag to both the :class:`.TypeDecorator` - and :class:`.UserDefinedType` classes. - - .. seealso:: - - :ref:`sql_caching` - - """ # noqa: E501 - - @util.non_memoized_property - def _static_cache_key( - self, - ) -> Union[CacheConst, Tuple[Any, ...]]: - cache_ok = self.__class__.__dict__.get("cache_ok", None) - - if cache_ok is None: - for subtype in self.__class__.__mro__: - if ExternalType in subtype.__bases__: - break - else: - subtype = self.__class__.__mro__[1] - - util.warn( - "%s %r will not produce a cache key because " - "the ``cache_ok`` attribute is not set to True. This can " - "have significant performance implications including some " - "performance degradations in comparison to prior SQLAlchemy " - "versions. Set this attribute to True if this type object's " - "state is safe to use in a cache key, or False to " - "disable this warning." % (subtype.__name__, self), - code="cprf", - ) - elif cache_ok is True: - return super()._static_cache_key - - return NO_CACHE - - -class UserDefinedType( - ExternalType, TypeEngineMixin, TypeEngine[_T], util.EnsureKWArg -): - """Base for user defined types. - - This should be the base of new types. Note that - for most cases, :class:`.TypeDecorator` is probably - more appropriate:: - - import sqlalchemy.types as types - - class MyType(types.UserDefinedType): - cache_ok = True - - def __init__(self, precision = 8): - self.precision = precision - - def get_col_spec(self, **kw): - return "MYTYPE(%s)" % self.precision - - def bind_processor(self, dialect): - def process(value): - return value - return process - - def result_processor(self, dialect, coltype): - def process(value): - return value - return process - - Once the type is made, it's immediately usable:: - - table = Table('foo', metadata_obj, - Column('id', Integer, primary_key=True), - Column('data', MyType(16)) - ) - - The ``get_col_spec()`` method will in most cases receive a keyword - argument ``type_expression`` which refers to the owning expression - of the type as being compiled, such as a :class:`_schema.Column` or - :func:`.cast` construct. This keyword is only sent if the method - accepts keyword arguments (e.g. ``**kw``) in its argument signature; - introspection is used to check for this in order to support legacy - forms of this function. - - The :attr:`.UserDefinedType.cache_ok` class-level flag indicates if this - custom :class:`.UserDefinedType` is safe to be used as part of a cache key. - This flag defaults to ``None`` which will initially generate a warning - when the SQL compiler attempts to generate a cache key for a statement - that uses this type. If the :class:`.UserDefinedType` is not guaranteed - to produce the same bind/result behavior and SQL generation - every time, this flag should be set to ``False``; otherwise if the - class produces the same behavior each time, it may be set to ``True``. - See :attr:`.UserDefinedType.cache_ok` for further notes on how this works. - - .. versionadded:: 1.4.28 Generalized the :attr:`.ExternalType.cache_ok` - flag so that it is available for both :class:`.TypeDecorator` as well - as :class:`.UserDefinedType`. - - """ - - __visit_name__ = "user_defined" - - ensure_kwarg = "get_col_spec" - - def coerce_compared_value( - self, op: Optional[OperatorType], value: Any - ) -> TypeEngine[Any]: - """Suggest a type for a 'coerced' Python value in an expression. - - Default behavior for :class:`.UserDefinedType` is the - same as that of :class:`.TypeDecorator`; by default it returns - ``self``, assuming the compared value should be coerced into - the same type as this one. See - :meth:`.TypeDecorator.coerce_compared_value` for more detail. - - """ - - return self - - -class Emulated(TypeEngineMixin): - """Mixin for base types that emulate the behavior of a DB-native type. - - An :class:`.Emulated` type will use an available database type - in conjunction with Python-side routines and/or database constraints - in order to approximate the behavior of a database type that is provided - natively by some backends. When a native-providing backend is in - use, the native version of the type is used. This native version - should include the :class:`.NativeForEmulated` mixin to allow it to be - distinguished from :class:`.Emulated`. - - Current examples of :class:`.Emulated` are: :class:`.Interval`, - :class:`.Enum`, :class:`.Boolean`. - - .. versionadded:: 1.2.0b3 - - """ - - native: bool - - def adapt_to_emulated( - self, - impltype: Type[Union[TypeEngine[Any], TypeEngineMixin]], - **kw: Any, - ) -> TypeEngine[Any]: - """Given an impl class, adapt this type to the impl assuming - "emulated". - - The impl should also be an "emulated" version of this type, - most likely the same class as this type itself. - - e.g.: sqltypes.Enum adapts to the Enum class. - - """ - return super().adapt(impltype, **kw) - - @overload - def adapt(self, cls: Type[_TE], **kw: Any) -> _TE: ... - - @overload - def adapt( - self, cls: Type[TypeEngineMixin], **kw: Any - ) -> TypeEngine[Any]: ... - - def adapt( - self, cls: Type[Union[TypeEngine[Any], TypeEngineMixin]], **kw: Any - ) -> TypeEngine[Any]: - if _is_native_for_emulated(cls): - if self.native: - # native support requested, dialect gave us a native - # implementor, pass control over to it - return cls.adapt_emulated_to_native(self, **kw) - else: - # non-native support, let the native implementor - # decide also, at the moment this is just to help debugging - # as only the default logic is implemented. - return cls.adapt_native_to_emulated(self, **kw) - else: - # this would be, both classes are Enum, or both classes - # are postgresql.ENUM - if issubclass(cls, self.__class__): - return self.adapt_to_emulated(cls, **kw) - else: - return super().adapt(cls, **kw) - - -def _is_native_for_emulated( - typ: Type[Union[TypeEngine[Any], TypeEngineMixin]], -) -> TypeGuard[Type[NativeForEmulated]]: - return hasattr(typ, "adapt_emulated_to_native") - - -class NativeForEmulated(TypeEngineMixin): - """Indicates DB-native types supported by an :class:`.Emulated` type. - - .. versionadded:: 1.2.0b3 - - """ - - @classmethod - def adapt_native_to_emulated( - cls, - impl: Union[TypeEngine[Any], TypeEngineMixin], - **kw: Any, - ) -> TypeEngine[Any]: - """Given an impl, adapt this type's class to the impl assuming - "emulated". - - - """ - impltype = impl.__class__ - return impl.adapt(impltype, **kw) - - @classmethod - def adapt_emulated_to_native( - cls, - impl: Union[TypeEngine[Any], TypeEngineMixin], - **kw: Any, - ) -> TypeEngine[Any]: - """Given an impl, adapt this type's class to the impl assuming - "native". - - The impl will be an :class:`.Emulated` class but not a - :class:`.NativeForEmulated`. - - e.g.: postgresql.ENUM produces a type given an Enum instance. - - """ - - # dmypy seems to crash on this - return cls(**kw) # type: ignore - - # dmypy seems to crash with this, on repeated runs with changes - # if TYPE_CHECKING: - # def __init__(self, **kw: Any): - # ... - - -class TypeDecorator(SchemaEventTarget, ExternalType, TypeEngine[_T]): - """Allows the creation of types which add additional functionality - to an existing type. - - This method is preferred to direct subclassing of SQLAlchemy's - built-in types as it ensures that all required functionality of - the underlying type is kept in place. - - Typical usage:: - - import sqlalchemy.types as types - - class MyType(types.TypeDecorator): - '''Prefixes Unicode values with "PREFIX:" on the way in and - strips it off on the way out. - ''' - - impl = types.Unicode - - cache_ok = True - - def process_bind_param(self, value, dialect): - return "PREFIX:" + value - - def process_result_value(self, value, dialect): - return value[7:] - - def copy(self, **kw): - return MyType(self.impl.length) - - The class-level ``impl`` attribute is required, and can reference any - :class:`.TypeEngine` class. Alternatively, the :meth:`load_dialect_impl` - method can be used to provide different type classes based on the dialect - given; in this case, the ``impl`` variable can reference - ``TypeEngine`` as a placeholder. - - The :attr:`.TypeDecorator.cache_ok` class-level flag indicates if this - custom :class:`.TypeDecorator` is safe to be used as part of a cache key. - This flag defaults to ``None`` which will initially generate a warning - when the SQL compiler attempts to generate a cache key for a statement - that uses this type. If the :class:`.TypeDecorator` is not guaranteed - to produce the same bind/result behavior and SQL generation - every time, this flag should be set to ``False``; otherwise if the - class produces the same behavior each time, it may be set to ``True``. - See :attr:`.TypeDecorator.cache_ok` for further notes on how this works. - - Types that receive a Python type that isn't similar to the ultimate type - used may want to define the :meth:`TypeDecorator.coerce_compared_value` - method. This is used to give the expression system a hint when coercing - Python objects into bind parameters within expressions. Consider this - expression:: - - mytable.c.somecol + datetime.date(2009, 5, 15) - - Above, if "somecol" is an ``Integer`` variant, it makes sense that - we're doing date arithmetic, where above is usually interpreted - by databases as adding a number of days to the given date. - The expression system does the right thing by not attempting to - coerce the "date()" value into an integer-oriented bind parameter. - - However, in the case of ``TypeDecorator``, we are usually changing an - incoming Python type to something new - ``TypeDecorator`` by default will - "coerce" the non-typed side to be the same type as itself. Such as below, - we define an "epoch" type that stores a date value as an integer:: - - class MyEpochType(types.TypeDecorator): - impl = types.Integer - - cache_ok = True - - epoch = datetime.date(1970, 1, 1) - - def process_bind_param(self, value, dialect): - return (value - self.epoch).days - - def process_result_value(self, value, dialect): - return self.epoch + timedelta(days=value) - - Our expression of ``somecol + date`` with the above type will coerce the - "date" on the right side to also be treated as ``MyEpochType``. - - This behavior can be overridden via the - :meth:`~TypeDecorator.coerce_compared_value` method, which returns a type - that should be used for the value of the expression. Below we set it such - that an integer value will be treated as an ``Integer``, and any other - value is assumed to be a date and will be treated as a ``MyEpochType``:: - - def coerce_compared_value(self, op, value): - if isinstance(value, int): - return Integer() - else: - return self - - .. warning:: - - Note that the **behavior of coerce_compared_value is not inherited - by default from that of the base type**. - If the :class:`.TypeDecorator` is augmenting a - type that requires special logic for certain types of operators, - this method **must** be overridden. A key example is when decorating - the :class:`_postgresql.JSON` and :class:`_postgresql.JSONB` types; - the default rules of :meth:`.TypeEngine.coerce_compared_value` should - be used in order to deal with operators like index operations:: - - from sqlalchemy import JSON - from sqlalchemy import TypeDecorator - - class MyJsonType(TypeDecorator): - impl = JSON - - cache_ok = True - - def coerce_compared_value(self, op, value): - return self.impl.coerce_compared_value(op, value) - - Without the above step, index operations such as ``mycol['foo']`` - will cause the index value ``'foo'`` to be JSON encoded. - - Similarly, when working with the :class:`.ARRAY` datatype, the - type coercion for index operations (e.g. ``mycol[5]``) is also - handled by :meth:`.TypeDecorator.coerce_compared_value`, where - again a simple override is sufficient unless special rules are needed - for particular operators:: - - from sqlalchemy import ARRAY - from sqlalchemy import TypeDecorator - - class MyArrayType(TypeDecorator): - impl = ARRAY - - cache_ok = True - - def coerce_compared_value(self, op, value): - return self.impl.coerce_compared_value(op, value) - - - """ - - __visit_name__ = "type_decorator" - - _is_type_decorator = True - - # this is that pattern I've used in a few places (Dialect.dbapi, - # Dialect.type_compiler) where the "cls.attr" is a class to make something, - # and "instance.attr" is an instance of that thing. It's such a nifty, - # great pattern, and there is zero chance Python typing tools will ever be - # OK with it. For TypeDecorator.impl, this is a highly public attribute so - # we really can't change its behavior without a major deprecation routine. - impl: Union[TypeEngine[Any], Type[TypeEngine[Any]]] - - # we are changing its behavior *slightly*, which is that we now consume - # the instance level version from this memoized property instead, so you - # can't reassign "impl" on an existing TypeDecorator that's already been - # used (something one shouldn't do anyway) without also updating - # impl_instance. - @util.memoized_property - def impl_instance(self) -> TypeEngine[Any]: - return self.impl # type: ignore - - def __init__(self, *args: Any, **kwargs: Any): - """Construct a :class:`.TypeDecorator`. - - Arguments sent here are passed to the constructor - of the class assigned to the ``impl`` class level attribute, - assuming the ``impl`` is a callable, and the resulting - object is assigned to the ``self.impl`` instance attribute - (thus overriding the class attribute of the same name). - - If the class level ``impl`` is not a callable (the unusual case), - it will be assigned to the same instance attribute 'as-is', - ignoring those arguments passed to the constructor. - - Subclasses can override this to customize the generation - of ``self.impl`` entirely. - - """ - - if not hasattr(self.__class__, "impl"): - raise AssertionError( - "TypeDecorator implementations " - "require a class-level variable " - "'impl' which refers to the class of " - "type being decorated" - ) - - self.impl = to_instance(self.__class__.impl, *args, **kwargs) - - coerce_to_is_types: Sequence[Type[Any]] = (type(None),) - """Specify those Python types which should be coerced at the expression - level to "IS <constant>" when compared using ``==`` (and same for - ``IS NOT`` in conjunction with ``!=``). - - For most SQLAlchemy types, this includes ``NoneType``, as well as - ``bool``. - - :class:`.TypeDecorator` modifies this list to only include ``NoneType``, - as typedecorator implementations that deal with boolean types are common. - - Custom :class:`.TypeDecorator` classes can override this attribute to - return an empty tuple, in which case no values will be coerced to - constants. - - """ - - class Comparator(TypeEngine.Comparator[_CT]): - """A :class:`.TypeEngine.Comparator` that is specific to - :class:`.TypeDecorator`. - - User-defined :class:`.TypeDecorator` classes should not typically - need to modify this. - - - """ - - __slots__ = () - - def operate( - self, op: OperatorType, *other: Any, **kwargs: Any - ) -> ColumnElement[_CT]: - if TYPE_CHECKING: - assert isinstance(self.expr.type, TypeDecorator) - kwargs["_python_is_types"] = self.expr.type.coerce_to_is_types - return super().operate(op, *other, **kwargs) - - def reverse_operate( - self, op: OperatorType, other: Any, **kwargs: Any - ) -> ColumnElement[_CT]: - if TYPE_CHECKING: - assert isinstance(self.expr.type, TypeDecorator) - kwargs["_python_is_types"] = self.expr.type.coerce_to_is_types - return super().reverse_operate(op, other, **kwargs) - - @property - def comparator_factory( # type: ignore # mypy properties bug - self, - ) -> _ComparatorFactory[Any]: - if TypeDecorator.Comparator in self.impl.comparator_factory.__mro__: # type: ignore # noqa: E501 - return self.impl.comparator_factory - else: - # reconcile the Comparator class on the impl with that - # of TypeDecorator - return type( - "TDComparator", - (TypeDecorator.Comparator, self.impl.comparator_factory), # type: ignore # noqa: E501 - {}, - ) - - def _gen_dialect_impl(self, dialect: Dialect) -> TypeEngine[_T]: - if dialect.name in self._variant_mapping: - adapted = dialect.type_descriptor( - self._variant_mapping[dialect.name] - ) - else: - adapted = dialect.type_descriptor(self) - if adapted is not self: - return adapted - - # otherwise adapt the impl type, link - # to a copy of this TypeDecorator and return - # that. - typedesc = self.load_dialect_impl(dialect).dialect_impl(dialect) - tt = self.copy() - if not isinstance(tt, self.__class__): - raise AssertionError( - "Type object %s does not properly " - "implement the copy() method, it must " - "return an object of type %s" % (self, self.__class__) - ) - tt.impl = tt.impl_instance = typedesc - return tt - - @util.ro_non_memoized_property - def _type_affinity(self) -> Optional[Type[TypeEngine[Any]]]: - return self.impl_instance._type_affinity - - def _set_parent( - self, parent: SchemaEventTarget, outer: bool = False, **kw: Any - ) -> None: - """Support SchemaEventTarget""" - - super()._set_parent(parent) - - if not outer and isinstance(self.impl_instance, SchemaEventTarget): - self.impl_instance._set_parent(parent, outer=False, **kw) - - def _set_parent_with_dispatch( - self, parent: SchemaEventTarget, **kw: Any - ) -> None: - """Support SchemaEventTarget""" - - super()._set_parent_with_dispatch(parent, outer=True, **kw) - - if isinstance(self.impl_instance, SchemaEventTarget): - self.impl_instance._set_parent_with_dispatch(parent) - - def type_engine(self, dialect: Dialect) -> TypeEngine[Any]: - """Return a dialect-specific :class:`.TypeEngine` instance - for this :class:`.TypeDecorator`. - - In most cases this returns a dialect-adapted form of - the :class:`.TypeEngine` type represented by ``self.impl``. - Makes usage of :meth:`dialect_impl`. - Behavior can be customized here by overriding - :meth:`load_dialect_impl`. - - """ - adapted = dialect.type_descriptor(self) - if not isinstance(adapted, type(self)): - return adapted - else: - return self.load_dialect_impl(dialect) - - def load_dialect_impl(self, dialect: Dialect) -> TypeEngine[Any]: - """Return a :class:`.TypeEngine` object corresponding to a dialect. - - This is an end-user override hook that can be used to provide - differing types depending on the given dialect. It is used - by the :class:`.TypeDecorator` implementation of :meth:`type_engine` - to help determine what type should ultimately be returned - for a given :class:`.TypeDecorator`. - - By default returns ``self.impl``. - - """ - return self.impl_instance - - def _unwrapped_dialect_impl(self, dialect: Dialect) -> TypeEngine[Any]: - """Return the 'unwrapped' dialect impl for this type. - - This is used by the :meth:`.DefaultDialect.set_input_sizes` - method. - - """ - # some dialects have a lookup for a TypeDecorator subclass directly. - # postgresql.INTERVAL being the main example - typ = self.dialect_impl(dialect) - - # if we are still a type decorator, load the per-dialect switch - # (such as what Variant uses), then get the dialect impl for that. - if isinstance(typ, self.__class__): - return typ.load_dialect_impl(dialect).dialect_impl(dialect) - else: - return typ - - def __getattr__(self, key: str) -> Any: - """Proxy all other undefined accessors to the underlying - implementation.""" - return getattr(self.impl_instance, key) - - def process_literal_param( - self, value: Optional[_T], dialect: Dialect - ) -> str: - """Receive a literal parameter value to be rendered inline within - a statement. - - .. note:: - - This method is called during the **SQL compilation** phase of a - statement, when rendering a SQL string. Unlike other SQL - compilation methods, it is passed a specific Python value to be - rendered as a string. However it should not be confused with the - :meth:`_types.TypeDecorator.process_bind_param` method, which is - the more typical method that processes the actual value passed to a - particular parameter at statement execution time. - - Custom subclasses of :class:`_types.TypeDecorator` should override - this method to provide custom behaviors for incoming data values - that are in the special case of being rendered as literals. - - The returned string will be rendered into the output string. - - """ - raise NotImplementedError() - - def process_bind_param(self, value: Optional[_T], dialect: Dialect) -> Any: - """Receive a bound parameter value to be converted. - - Custom subclasses of :class:`_types.TypeDecorator` should override - this method to provide custom behaviors for incoming data values. - This method is called at **statement execution time** and is passed - the literal Python data value which is to be associated with a bound - parameter in the statement. - - The operation could be anything desired to perform custom - behavior, such as transforming or serializing data. - This could also be used as a hook for validating logic. - - :param value: Data to operate upon, of any type expected by - this method in the subclass. Can be ``None``. - :param dialect: the :class:`.Dialect` in use. - - .. seealso:: - - :ref:`types_typedecorator` - - :meth:`_types.TypeDecorator.process_result_value` - - """ - - raise NotImplementedError() - - def process_result_value( - self, value: Optional[Any], dialect: Dialect - ) -> Optional[_T]: - """Receive a result-row column value to be converted. - - Custom subclasses of :class:`_types.TypeDecorator` should override - this method to provide custom behaviors for data values - being received in result rows coming from the database. - This method is called at **result fetching time** and is passed - the literal Python data value that's extracted from a database result - row. - - The operation could be anything desired to perform custom - behavior, such as transforming or deserializing data. - - :param value: Data to operate upon, of any type expected by - this method in the subclass. Can be ``None``. - :param dialect: the :class:`.Dialect` in use. - - .. seealso:: - - :ref:`types_typedecorator` - - :meth:`_types.TypeDecorator.process_bind_param` - - - """ - - raise NotImplementedError() - - @util.memoized_property - def _has_bind_processor(self) -> bool: - """memoized boolean, check if process_bind_param is implemented. - - Allows the base process_bind_param to raise - NotImplementedError without needing to test an expensive - exception throw. - - """ - - return util.method_is_overridden( - self, TypeDecorator.process_bind_param - ) - - @util.memoized_property - def _has_literal_processor(self) -> bool: - """memoized boolean, check if process_literal_param is implemented.""" - - return util.method_is_overridden( - self, TypeDecorator.process_literal_param - ) - - def literal_processor( - self, dialect: Dialect - ) -> Optional[_LiteralProcessorType[_T]]: - """Provide a literal processing function for the given - :class:`.Dialect`. - - This is the method that fulfills the :class:`.TypeEngine` - contract for literal value conversion which normally occurs via - the :meth:`_types.TypeEngine.literal_processor` method. - - .. note:: - - User-defined subclasses of :class:`_types.TypeDecorator` should - **not** implement this method, and should instead implement - :meth:`_types.TypeDecorator.process_literal_param` so that the - "inner" processing provided by the implementing type is maintained. - - """ - - if self._has_literal_processor: - process_literal_param = self.process_literal_param - process_bind_param = None - elif self._has_bind_processor: - # use the bind processor if dont have a literal processor, - # but we have an impl literal processor - process_literal_param = None - process_bind_param = self.process_bind_param - else: - process_literal_param = None - process_bind_param = None - - if process_literal_param is not None: - impl_processor = self.impl_instance.literal_processor(dialect) - if impl_processor: - fixed_impl_processor = impl_processor - fixed_process_literal_param = process_literal_param - - def process(value: Any) -> str: - return fixed_impl_processor( - fixed_process_literal_param(value, dialect) - ) - - else: - fixed_process_literal_param = process_literal_param - - def process(value: Any) -> str: - return fixed_process_literal_param(value, dialect) - - return process - - elif process_bind_param is not None: - impl_processor = self.impl_instance.literal_processor(dialect) - if not impl_processor: - return None - else: - fixed_impl_processor = impl_processor - fixed_process_bind_param = process_bind_param - - def process(value: Any) -> str: - return fixed_impl_processor( - fixed_process_bind_param(value, dialect) - ) - - return process - else: - return self.impl_instance.literal_processor(dialect) - - def bind_processor( - self, dialect: Dialect - ) -> Optional[_BindProcessorType[_T]]: - """Provide a bound value processing function for the - given :class:`.Dialect`. - - This is the method that fulfills the :class:`.TypeEngine` - contract for bound value conversion which normally occurs via - the :meth:`_types.TypeEngine.bind_processor` method. - - .. note:: - - User-defined subclasses of :class:`_types.TypeDecorator` should - **not** implement this method, and should instead implement - :meth:`_types.TypeDecorator.process_bind_param` so that the "inner" - processing provided by the implementing type is maintained. - - :param dialect: Dialect instance in use. - - """ - if self._has_bind_processor: - process_param = self.process_bind_param - impl_processor = self.impl_instance.bind_processor(dialect) - if impl_processor: - fixed_impl_processor = impl_processor - fixed_process_param = process_param - - def process(value: Optional[_T]) -> Any: - return fixed_impl_processor( - fixed_process_param(value, dialect) - ) - - else: - fixed_process_param = process_param - - def process(value: Optional[_T]) -> Any: - return fixed_process_param(value, dialect) - - return process - else: - return self.impl_instance.bind_processor(dialect) - - @util.memoized_property - def _has_result_processor(self) -> bool: - """memoized boolean, check if process_result_value is implemented. - - Allows the base process_result_value to raise - NotImplementedError without needing to test an expensive - exception throw. - - """ - - return util.method_is_overridden( - self, TypeDecorator.process_result_value - ) - - def result_processor( - self, dialect: Dialect, coltype: Any - ) -> Optional[_ResultProcessorType[_T]]: - """Provide a result value processing function for the given - :class:`.Dialect`. - - This is the method that fulfills the :class:`.TypeEngine` - contract for bound value conversion which normally occurs via - the :meth:`_types.TypeEngine.result_processor` method. - - .. note:: - - User-defined subclasses of :class:`_types.TypeDecorator` should - **not** implement this method, and should instead implement - :meth:`_types.TypeDecorator.process_result_value` so that the - "inner" processing provided by the implementing type is maintained. - - :param dialect: Dialect instance in use. - :param coltype: A SQLAlchemy data type - - """ - if self._has_result_processor: - process_value = self.process_result_value - impl_processor = self.impl_instance.result_processor( - dialect, coltype - ) - if impl_processor: - fixed_process_value = process_value - fixed_impl_processor = impl_processor - - def process(value: Any) -> Optional[_T]: - return fixed_process_value( - fixed_impl_processor(value), dialect - ) - - else: - fixed_process_value = process_value - - def process(value: Any) -> Optional[_T]: - return fixed_process_value(value, dialect) - - return process - else: - return self.impl_instance.result_processor(dialect, coltype) - - @util.memoized_property - def _has_bind_expression(self) -> bool: - return ( - util.method_is_overridden(self, TypeDecorator.bind_expression) - or self.impl_instance._has_bind_expression - ) - - def bind_expression( - self, bindparam: BindParameter[_T] - ) -> Optional[ColumnElement[_T]]: - """Given a bind value (i.e. a :class:`.BindParameter` instance), - return a SQL expression which will typically wrap the given parameter. - - .. note:: - - This method is called during the **SQL compilation** phase of a - statement, when rendering a SQL string. It is **not** necessarily - called against specific values, and should not be confused with the - :meth:`_types.TypeDecorator.process_bind_param` method, which is - the more typical method that processes the actual value passed to a - particular parameter at statement execution time. - - Subclasses of :class:`_types.TypeDecorator` can override this method - to provide custom bind expression behavior for the type. This - implementation will **replace** that of the underlying implementation - type. - - """ - return self.impl_instance.bind_expression(bindparam) - - @util.memoized_property - def _has_column_expression(self) -> bool: - """memoized boolean, check if column_expression is implemented. - - Allows the method to be skipped for the vast majority of expression - types that don't use this feature. - - """ - - return ( - util.method_is_overridden(self, TypeDecorator.column_expression) - or self.impl_instance._has_column_expression - ) - - def column_expression( - self, column: ColumnElement[_T] - ) -> Optional[ColumnElement[_T]]: - """Given a SELECT column expression, return a wrapping SQL expression. - - .. note:: - - This method is called during the **SQL compilation** phase of a - statement, when rendering a SQL string. It is **not** called - against specific values, and should not be confused with the - :meth:`_types.TypeDecorator.process_result_value` method, which is - the more typical method that processes the actual value returned - in a result row subsequent to statement execution time. - - Subclasses of :class:`_types.TypeDecorator` can override this method - to provide custom column expression behavior for the type. This - implementation will **replace** that of the underlying implementation - type. - - See the description of :meth:`_types.TypeEngine.column_expression` - for a complete description of the method's use. - - """ - - return self.impl_instance.column_expression(column) - - def coerce_compared_value( - self, op: Optional[OperatorType], value: Any - ) -> Any: - """Suggest a type for a 'coerced' Python value in an expression. - - By default, returns self. This method is called by - the expression system when an object using this type is - on the left or right side of an expression against a plain Python - object which does not yet have a SQLAlchemy type assigned:: - - expr = table.c.somecolumn + 35 - - Where above, if ``somecolumn`` uses this type, this method will - be called with the value ``operator.add`` - and ``35``. The return value is whatever SQLAlchemy type should - be used for ``35`` for this particular operation. - - """ - return self - - def copy(self, **kw: Any) -> Self: - """Produce a copy of this :class:`.TypeDecorator` instance. - - This is a shallow copy and is provided to fulfill part of - the :class:`.TypeEngine` contract. It usually does not - need to be overridden unless the user-defined :class:`.TypeDecorator` - has local state that should be deep-copied. - - """ - - instance = self.__class__.__new__(self.__class__) - instance.__dict__.update(self.__dict__) - return instance - - def get_dbapi_type(self, dbapi: ModuleType) -> Optional[Any]: - """Return the DBAPI type object represented by this - :class:`.TypeDecorator`. - - By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the - underlying "impl". - """ - return self.impl_instance.get_dbapi_type(dbapi) - - def compare_values(self, x: Any, y: Any) -> bool: - """Given two values, compare them for equality. - - By default this calls upon :meth:`.TypeEngine.compare_values` - of the underlying "impl", which in turn usually - uses the Python equals operator ``==``. - - This function is used by the ORM to compare - an original-loaded value with an intercepted - "changed" value, to determine if a net change - has occurred. - - """ - return self.impl_instance.compare_values(x, y) - - # mypy property bug - @property - def sort_key_function(self) -> Optional[Callable[[Any], Any]]: # type: ignore # noqa: E501 - return self.impl_instance.sort_key_function - - def __repr__(self) -> str: - return util.generic_repr(self, to_inspect=self.impl_instance) - - -class Variant(TypeDecorator[_T]): - """deprecated. symbol is present for backwards-compatibility with - workaround recipes, however this actual type should not be used. - - """ - - def __init__(self, *arg: Any, **kw: Any): - raise NotImplementedError( - "Variant is no longer used in SQLAlchemy; this is a " - "placeholder symbol for backwards compatibility." - ) - - -@overload -def to_instance( - typeobj: Union[Type[_TE], _TE], *arg: Any, **kw: Any -) -> _TE: ... - - -@overload -def to_instance(typeobj: None, *arg: Any, **kw: Any) -> TypeEngine[None]: ... - - -def to_instance( - typeobj: Union[Type[_TE], _TE, None], *arg: Any, **kw: Any -) -> Union[_TE, TypeEngine[None]]: - if typeobj is None: - return NULLTYPE - - if callable(typeobj): - return typeobj(*arg, **kw) - else: - return typeobj - - -def adapt_type( - typeobj: TypeEngine[Any], - colspecs: Mapping[Type[Any], Type[TypeEngine[Any]]], -) -> TypeEngine[Any]: - if isinstance(typeobj, type): - typeobj = typeobj() - for t in typeobj.__class__.__mro__[0:-1]: - try: - impltype = colspecs[t] - break - except KeyError: - pass - else: - # couldn't adapt - so just return the type itself - # (it may be a user-defined type) - return typeobj - # if we adapted the given generic type to a database-specific type, - # but it turns out the originally given "generic" type - # is actually a subclass of our resulting type, then we were already - # given a more specific type than that required; so use that. - if issubclass(typeobj.__class__, impltype): - return typeobj - return typeobj.adapt(impltype) diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/util.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/util.py deleted file mode 100644 index 4a35bb2..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/util.py +++ /dev/null @@ -1,1486 +0,0 @@ -# sql/util.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: allow-untyped-defs, allow-untyped-calls - -"""High level utilities which build upon other modules here. - -""" -from __future__ import annotations - -from collections import deque -import copy -from itertools import chain -import typing -from typing import AbstractSet -from typing import Any -from typing import Callable -from typing import cast -from typing import Collection -from typing import Dict -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Optional -from typing import overload -from typing import Sequence -from typing import Tuple -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from . import coercions -from . import operators -from . import roles -from . import visitors -from ._typing import is_text_clause -from .annotation import _deep_annotate as _deep_annotate # noqa: F401 -from .annotation import _deep_deannotate as _deep_deannotate # noqa: F401 -from .annotation import _shallow_annotate as _shallow_annotate # noqa: F401 -from .base import _expand_cloned -from .base import _from_objects -from .cache_key import HasCacheKey as HasCacheKey # noqa: F401 -from .ddl import sort_tables as sort_tables # noqa: F401 -from .elements import _find_columns as _find_columns -from .elements import _label_reference -from .elements import _textual_label_reference -from .elements import BindParameter -from .elements import ClauseElement -from .elements import ColumnClause -from .elements import ColumnElement -from .elements import Grouping -from .elements import KeyedColumnElement -from .elements import Label -from .elements import NamedColumn -from .elements import Null -from .elements import UnaryExpression -from .schema import Column -from .selectable import Alias -from .selectable import FromClause -from .selectable import FromGrouping -from .selectable import Join -from .selectable import ScalarSelect -from .selectable import SelectBase -from .selectable import TableClause -from .visitors import _ET -from .. import exc -from .. import util -from ..util.typing import Literal -from ..util.typing import Protocol - -if typing.TYPE_CHECKING: - from ._typing import _EquivalentColumnMap - from ._typing import _LimitOffsetType - from ._typing import _TypeEngineArgument - from .elements import BinaryExpression - from .elements import TextClause - from .selectable import _JoinTargetElement - from .selectable import _SelectIterable - from .selectable import Selectable - from .visitors import _TraverseCallableType - from .visitors import ExternallyTraversible - from .visitors import ExternalTraversal - from ..engine.interfaces import _AnyExecuteParams - from ..engine.interfaces import _AnyMultiExecuteParams - from ..engine.interfaces import _AnySingleExecuteParams - from ..engine.interfaces import _CoreSingleExecuteParams - from ..engine.row import Row - -_CE = TypeVar("_CE", bound="ColumnElement[Any]") - - -def join_condition( - a: FromClause, - b: FromClause, - a_subset: Optional[FromClause] = None, - consider_as_foreign_keys: Optional[AbstractSet[ColumnClause[Any]]] = None, -) -> ColumnElement[bool]: - """Create a join condition between two tables or selectables. - - e.g.:: - - join_condition(tablea, tableb) - - would produce an expression along the lines of:: - - tablea.c.id==tableb.c.tablea_id - - The join is determined based on the foreign key relationships - between the two selectables. If there are multiple ways - to join, or no way to join, an error is raised. - - :param a_subset: An optional expression that is a sub-component - of ``a``. An attempt will be made to join to just this sub-component - first before looking at the full ``a`` construct, and if found - will be successful even if there are other ways to join to ``a``. - This allows the "right side" of a join to be passed thereby - providing a "natural join". - - """ - return Join._join_condition( - a, - b, - a_subset=a_subset, - consider_as_foreign_keys=consider_as_foreign_keys, - ) - - -def find_join_source( - clauses: List[FromClause], join_to: FromClause -) -> List[int]: - """Given a list of FROM clauses and a selectable, - return the first index and element from the list of - clauses which can be joined against the selectable. returns - None, None if no match is found. - - e.g.:: - - clause1 = table1.join(table2) - clause2 = table4.join(table5) - - join_to = table2.join(table3) - - find_join_source([clause1, clause2], join_to) == clause1 - - """ - - selectables = list(_from_objects(join_to)) - idx = [] - for i, f in enumerate(clauses): - for s in selectables: - if f.is_derived_from(s): - idx.append(i) - return idx - - -def find_left_clause_that_matches_given( - clauses: Sequence[FromClause], join_from: FromClause -) -> List[int]: - """Given a list of FROM clauses and a selectable, - return the indexes from the list of - clauses which is derived from the selectable. - - """ - - selectables = list(_from_objects(join_from)) - liberal_idx = [] - for i, f in enumerate(clauses): - for s in selectables: - # basic check, if f is derived from s. - # this can be joins containing a table, or an aliased table - # or select statement matching to a table. This check - # will match a table to a selectable that is adapted from - # that table. With Query, this suits the case where a join - # is being made to an adapted entity - if f.is_derived_from(s): - liberal_idx.append(i) - break - - # in an extremely small set of use cases, a join is being made where - # there are multiple FROM clauses where our target table is represented - # in more than one, such as embedded or similar. in this case, do - # another pass where we try to get a more exact match where we aren't - # looking at adaption relationships. - if len(liberal_idx) > 1: - conservative_idx = [] - for idx in liberal_idx: - f = clauses[idx] - for s in selectables: - if set(surface_selectables(f)).intersection( - surface_selectables(s) - ): - conservative_idx.append(idx) - break - if conservative_idx: - return conservative_idx - - return liberal_idx - - -def find_left_clause_to_join_from( - clauses: Sequence[FromClause], - join_to: _JoinTargetElement, - onclause: Optional[ColumnElement[Any]], -) -> List[int]: - """Given a list of FROM clauses, a selectable, - and optional ON clause, return a list of integer indexes from the - clauses list indicating the clauses that can be joined from. - - The presence of an "onclause" indicates that at least one clause can - definitely be joined from; if the list of clauses is of length one - and the onclause is given, returns that index. If the list of clauses - is more than length one, and the onclause is given, attempts to locate - which clauses contain the same columns. - - """ - idx = [] - selectables = set(_from_objects(join_to)) - - # if we are given more than one target clause to join - # from, use the onclause to provide a more specific answer. - # otherwise, don't try to limit, after all, "ON TRUE" is a valid - # on clause - if len(clauses) > 1 and onclause is not None: - resolve_ambiguity = True - cols_in_onclause = _find_columns(onclause) - else: - resolve_ambiguity = False - cols_in_onclause = None - - for i, f in enumerate(clauses): - for s in selectables.difference([f]): - if resolve_ambiguity: - assert cols_in_onclause is not None - if set(f.c).union(s.c).issuperset(cols_in_onclause): - idx.append(i) - break - elif onclause is not None or Join._can_join(f, s): - idx.append(i) - break - - if len(idx) > 1: - # this is the same "hide froms" logic from - # Selectable._get_display_froms - toremove = set( - chain(*[_expand_cloned(f._hide_froms) for f in clauses]) - ) - idx = [i for i in idx if clauses[i] not in toremove] - - # onclause was given and none of them resolved, so assume - # all indexes can match - if not idx and onclause is not None: - return list(range(len(clauses))) - else: - return idx - - -def visit_binary_product( - fn: Callable[ - [BinaryExpression[Any], ColumnElement[Any], ColumnElement[Any]], None - ], - expr: ColumnElement[Any], -) -> None: - """Produce a traversal of the given expression, delivering - column comparisons to the given function. - - The function is of the form:: - - def my_fn(binary, left, right) - - For each binary expression located which has a - comparison operator, the product of "left" and - "right" will be delivered to that function, - in terms of that binary. - - Hence an expression like:: - - and_( - (a + b) == q + func.sum(e + f), - j == r - ) - - would have the traversal:: - - a <eq> q - a <eq> e - a <eq> f - b <eq> q - b <eq> e - b <eq> f - j <eq> r - - That is, every combination of "left" and - "right" that doesn't further contain - a binary comparison is passed as pairs. - - """ - stack: List[BinaryExpression[Any]] = [] - - def visit(element: ClauseElement) -> Iterator[ColumnElement[Any]]: - if isinstance(element, ScalarSelect): - # we don't want to dig into correlated subqueries, - # those are just column elements by themselves - yield element - elif element.__visit_name__ == "binary" and operators.is_comparison( - element.operator # type: ignore - ): - stack.insert(0, element) # type: ignore - for l in visit(element.left): # type: ignore - for r in visit(element.right): # type: ignore - fn(stack[0], l, r) - stack.pop(0) - for elem in element.get_children(): - visit(elem) - else: - if isinstance(element, ColumnClause): - yield element - for elem in element.get_children(): - yield from visit(elem) - - list(visit(expr)) - visit = None # type: ignore # remove gc cycles - - -def find_tables( - clause: ClauseElement, - *, - check_columns: bool = False, - include_aliases: bool = False, - include_joins: bool = False, - include_selects: bool = False, - include_crud: bool = False, -) -> List[TableClause]: - """locate Table objects within the given expression.""" - - tables: List[TableClause] = [] - _visitors: Dict[str, _TraverseCallableType[Any]] = {} - - if include_selects: - _visitors["select"] = _visitors["compound_select"] = tables.append - - if include_joins: - _visitors["join"] = tables.append - - if include_aliases: - _visitors["alias"] = _visitors["subquery"] = _visitors[ - "tablesample" - ] = _visitors["lateral"] = tables.append - - if include_crud: - _visitors["insert"] = _visitors["update"] = _visitors["delete"] = ( - lambda ent: tables.append(ent.table) - ) - - if check_columns: - - def visit_column(column): - tables.append(column.table) - - _visitors["column"] = visit_column - - _visitors["table"] = tables.append - - visitors.traverse(clause, {}, _visitors) - return tables - - -def unwrap_order_by(clause: Any) -> Any: - """Break up an 'order by' expression into individual column-expressions, - without DESC/ASC/NULLS FIRST/NULLS LAST""" - - cols = util.column_set() - result = [] - stack = deque([clause]) - - # examples - # column -> ASC/DESC == column - # column -> ASC/DESC -> label == column - # column -> label -> ASC/DESC -> label == column - # scalar_select -> label -> ASC/DESC == scalar_select -> label - - while stack: - t = stack.popleft() - if isinstance(t, ColumnElement) and ( - not isinstance(t, UnaryExpression) - or not operators.is_ordering_modifier(t.modifier) # type: ignore - ): - if isinstance(t, Label) and not isinstance( - t.element, ScalarSelect - ): - t = t.element - - if isinstance(t, Grouping): - t = t.element - - stack.append(t) - continue - elif isinstance(t, _label_reference): - t = t.element - - stack.append(t) - continue - if isinstance(t, (_textual_label_reference)): - continue - if t not in cols: - cols.add(t) - result.append(t) - - else: - for c in t.get_children(): - stack.append(c) - return result - - -def unwrap_label_reference(element): - def replace( - element: ExternallyTraversible, **kw: Any - ) -> Optional[ExternallyTraversible]: - if isinstance(element, _label_reference): - return element.element - elif isinstance(element, _textual_label_reference): - assert False, "can't unwrap a textual label reference" - return None - - return visitors.replacement_traverse(element, {}, replace) - - -def expand_column_list_from_order_by(collist, order_by): - """Given the columns clause and ORDER BY of a selectable, - return a list of column expressions that can be added to the collist - corresponding to the ORDER BY, without repeating those already - in the collist. - - """ - cols_already_present = { - col.element if col._order_by_label_element is not None else col - for col in collist - } - - to_look_for = list(chain(*[unwrap_order_by(o) for o in order_by])) - - return [col for col in to_look_for if col not in cols_already_present] - - -def clause_is_present(clause, search): - """Given a target clause and a second to search within, return True - if the target is plainly present in the search without any - subqueries or aliases involved. - - Basically descends through Joins. - - """ - - for elem in surface_selectables(search): - if clause == elem: # use == here so that Annotated's compare - return True - else: - return False - - -def tables_from_leftmost(clause: FromClause) -> Iterator[FromClause]: - if isinstance(clause, Join): - yield from tables_from_leftmost(clause.left) - yield from tables_from_leftmost(clause.right) - elif isinstance(clause, FromGrouping): - yield from tables_from_leftmost(clause.element) - else: - yield clause - - -def surface_selectables(clause): - stack = [clause] - while stack: - elem = stack.pop() - yield elem - if isinstance(elem, Join): - stack.extend((elem.left, elem.right)) - elif isinstance(elem, FromGrouping): - stack.append(elem.element) - - -def surface_selectables_only(clause): - stack = [clause] - while stack: - elem = stack.pop() - if isinstance(elem, (TableClause, Alias)): - yield elem - if isinstance(elem, Join): - stack.extend((elem.left, elem.right)) - elif isinstance(elem, FromGrouping): - stack.append(elem.element) - elif isinstance(elem, ColumnClause): - if elem.table is not None: - stack.append(elem.table) - else: - yield elem - elif elem is not None: - yield elem - - -def extract_first_column_annotation(column, annotation_name): - filter_ = (FromGrouping, SelectBase) - - stack = deque([column]) - while stack: - elem = stack.popleft() - if annotation_name in elem._annotations: - return elem._annotations[annotation_name] - for sub in elem.get_children(): - if isinstance(sub, filter_): - continue - stack.append(sub) - return None - - -def selectables_overlap(left: FromClause, right: FromClause) -> bool: - """Return True if left/right have some overlapping selectable""" - - return bool( - set(surface_selectables(left)).intersection(surface_selectables(right)) - ) - - -def bind_values(clause): - """Return an ordered list of "bound" values in the given clause. - - E.g.:: - - >>> expr = and_( - ... table.c.foo==5, table.c.foo==7 - ... ) - >>> bind_values(expr) - [5, 7] - """ - - v = [] - - def visit_bindparam(bind): - v.append(bind.effective_value) - - visitors.traverse(clause, {}, {"bindparam": visit_bindparam}) - return v - - -def _quote_ddl_expr(element): - if isinstance(element, str): - element = element.replace("'", "''") - return "'%s'" % element - else: - return repr(element) - - -class _repr_base: - _LIST: int = 0 - _TUPLE: int = 1 - _DICT: int = 2 - - __slots__ = ("max_chars",) - - max_chars: int - - def trunc(self, value: Any) -> str: - rep = repr(value) - lenrep = len(rep) - if lenrep > self.max_chars: - segment_length = self.max_chars // 2 - rep = ( - rep[0:segment_length] - + ( - " ... (%d characters truncated) ... " - % (lenrep - self.max_chars) - ) - + rep[-segment_length:] - ) - return rep - - -def _repr_single_value(value): - rp = _repr_base() - rp.max_chars = 300 - return rp.trunc(value) - - -class _repr_row(_repr_base): - """Provide a string view of a row.""" - - __slots__ = ("row",) - - def __init__(self, row: Row[Any], max_chars: int = 300): - self.row = row - self.max_chars = max_chars - - def __repr__(self) -> str: - trunc = self.trunc - return "(%s%s)" % ( - ", ".join(trunc(value) for value in self.row), - "," if len(self.row) == 1 else "", - ) - - -class _long_statement(str): - def __str__(self) -> str: - lself = len(self) - if lself > 500: - lleft = 250 - lright = 100 - trunc = lself - lleft - lright - return ( - f"{self[0:lleft]} ... {trunc} " - f"characters truncated ... {self[-lright:]}" - ) - else: - return str.__str__(self) - - -class _repr_params(_repr_base): - """Provide a string view of bound parameters. - - Truncates display to a given number of 'multi' parameter sets, - as well as long values to a given number of characters. - - """ - - __slots__ = "params", "batches", "ismulti", "max_params" - - def __init__( - self, - params: Optional[_AnyExecuteParams], - batches: int, - max_params: int = 100, - max_chars: int = 300, - ismulti: Optional[bool] = None, - ): - self.params = params - self.ismulti = ismulti - self.batches = batches - self.max_chars = max_chars - self.max_params = max_params - - def __repr__(self) -> str: - if self.ismulti is None: - return self.trunc(self.params) - - if isinstance(self.params, list): - typ = self._LIST - - elif isinstance(self.params, tuple): - typ = self._TUPLE - elif isinstance(self.params, dict): - typ = self._DICT - else: - return self.trunc(self.params) - - if self.ismulti: - multi_params = cast( - "_AnyMultiExecuteParams", - self.params, - ) - - if len(self.params) > self.batches: - msg = ( - " ... displaying %i of %i total bound parameter sets ... " - ) - return " ".join( - ( - self._repr_multi( - multi_params[: self.batches - 2], - typ, - )[0:-1], - msg % (self.batches, len(self.params)), - self._repr_multi(multi_params[-2:], typ)[1:], - ) - ) - else: - return self._repr_multi(multi_params, typ) - else: - return self._repr_params( - cast( - "_AnySingleExecuteParams", - self.params, - ), - typ, - ) - - def _repr_multi( - self, - multi_params: _AnyMultiExecuteParams, - typ: int, - ) -> str: - if multi_params: - if isinstance(multi_params[0], list): - elem_type = self._LIST - elif isinstance(multi_params[0], tuple): - elem_type = self._TUPLE - elif isinstance(multi_params[0], dict): - elem_type = self._DICT - else: - assert False, "Unknown parameter type %s" % ( - type(multi_params[0]) - ) - - elements = ", ".join( - self._repr_params(params, elem_type) for params in multi_params - ) - else: - elements = "" - - if typ == self._LIST: - return "[%s]" % elements - else: - return "(%s)" % elements - - def _get_batches(self, params: Iterable[Any]) -> Any: - lparams = list(params) - lenparams = len(lparams) - if lenparams > self.max_params: - lleft = self.max_params // 2 - return ( - lparams[0:lleft], - lparams[-lleft:], - lenparams - self.max_params, - ) - else: - return lparams, None, None - - def _repr_params( - self, - params: _AnySingleExecuteParams, - typ: int, - ) -> str: - if typ is self._DICT: - return self._repr_param_dict( - cast("_CoreSingleExecuteParams", params) - ) - elif typ is self._TUPLE: - return self._repr_param_tuple(cast("Sequence[Any]", params)) - else: - return self._repr_param_list(params) - - def _repr_param_dict(self, params: _CoreSingleExecuteParams) -> str: - trunc = self.trunc - ( - items_first_batch, - items_second_batch, - trunclen, - ) = self._get_batches(params.items()) - - if items_second_batch: - text = "{%s" % ( - ", ".join( - f"{key!r}: {trunc(value)}" - for key, value in items_first_batch - ) - ) - text += f" ... {trunclen} parameters truncated ... " - text += "%s}" % ( - ", ".join( - f"{key!r}: {trunc(value)}" - for key, value in items_second_batch - ) - ) - else: - text = "{%s}" % ( - ", ".join( - f"{key!r}: {trunc(value)}" - for key, value in items_first_batch - ) - ) - return text - - def _repr_param_tuple(self, params: Sequence[Any]) -> str: - trunc = self.trunc - - ( - items_first_batch, - items_second_batch, - trunclen, - ) = self._get_batches(params) - - if items_second_batch: - text = "(%s" % ( - ", ".join(trunc(value) for value in items_first_batch) - ) - text += f" ... {trunclen} parameters truncated ... " - text += "%s)" % ( - ", ".join(trunc(value) for value in items_second_batch), - ) - else: - text = "(%s%s)" % ( - ", ".join(trunc(value) for value in items_first_batch), - "," if len(items_first_batch) == 1 else "", - ) - return text - - def _repr_param_list(self, params: _AnySingleExecuteParams) -> str: - trunc = self.trunc - ( - items_first_batch, - items_second_batch, - trunclen, - ) = self._get_batches(params) - - if items_second_batch: - text = "[%s" % ( - ", ".join(trunc(value) for value in items_first_batch) - ) - text += f" ... {trunclen} parameters truncated ... " - text += "%s]" % ( - ", ".join(trunc(value) for value in items_second_batch) - ) - else: - text = "[%s]" % ( - ", ".join(trunc(value) for value in items_first_batch) - ) - return text - - -def adapt_criterion_to_null(crit: _CE, nulls: Collection[Any]) -> _CE: - """given criterion containing bind params, convert selected elements - to IS NULL. - - """ - - def visit_binary(binary): - if ( - isinstance(binary.left, BindParameter) - and binary.left._identifying_key in nulls - ): - # reverse order if the NULL is on the left side - binary.left = binary.right - binary.right = Null() - binary.operator = operators.is_ - binary.negate = operators.is_not - elif ( - isinstance(binary.right, BindParameter) - and binary.right._identifying_key in nulls - ): - binary.right = Null() - binary.operator = operators.is_ - binary.negate = operators.is_not - - return visitors.cloned_traverse(crit, {}, {"binary": visit_binary}) - - -def splice_joins( - left: Optional[FromClause], - right: Optional[FromClause], - stop_on: Optional[FromClause] = None, -) -> Optional[FromClause]: - if left is None: - return right - - stack: List[Tuple[Optional[FromClause], Optional[Join]]] = [(right, None)] - - adapter = ClauseAdapter(left) - ret = None - while stack: - (right, prevright) = stack.pop() - if isinstance(right, Join) and right is not stop_on: - right = right._clone() - right.onclause = adapter.traverse(right.onclause) - stack.append((right.left, right)) - else: - right = adapter.traverse(right) - if prevright is not None: - assert right is not None - prevright.left = right - if ret is None: - ret = right - - return ret - - -@overload -def reduce_columns( - columns: Iterable[ColumnElement[Any]], - *clauses: Optional[ClauseElement], - **kw: bool, -) -> Sequence[ColumnElement[Any]]: ... - - -@overload -def reduce_columns( - columns: _SelectIterable, - *clauses: Optional[ClauseElement], - **kw: bool, -) -> Sequence[Union[ColumnElement[Any], TextClause]]: ... - - -def reduce_columns( - columns: _SelectIterable, - *clauses: Optional[ClauseElement], - **kw: bool, -) -> Collection[Union[ColumnElement[Any], TextClause]]: - r"""given a list of columns, return a 'reduced' set based on natural - equivalents. - - the set is reduced to the smallest list of columns which have no natural - equivalent present in the list. A "natural equivalent" means that two - columns will ultimately represent the same value because they are related - by a foreign key. - - \*clauses is an optional list of join clauses which will be traversed - to further identify columns that are "equivalent". - - \**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys - whose tables are not yet configured, or columns that aren't yet present. - - This function is primarily used to determine the most minimal "primary - key" from a selectable, by reducing the set of primary key columns present - in the selectable to just those that are not repeated. - - """ - ignore_nonexistent_tables = kw.pop("ignore_nonexistent_tables", False) - only_synonyms = kw.pop("only_synonyms", False) - - column_set = util.OrderedSet(columns) - cset_no_text: util.OrderedSet[ColumnElement[Any]] = column_set.difference( - c for c in column_set if is_text_clause(c) # type: ignore - ) - - omit = util.column_set() - for col in cset_no_text: - for fk in chain(*[c.foreign_keys for c in col.proxy_set]): - for c in cset_no_text: - if c is col: - continue - try: - fk_col = fk.column - except exc.NoReferencedColumnError: - # TODO: add specific coverage here - # to test/sql/test_selectable ReduceTest - if ignore_nonexistent_tables: - continue - else: - raise - except exc.NoReferencedTableError: - # TODO: add specific coverage here - # to test/sql/test_selectable ReduceTest - if ignore_nonexistent_tables: - continue - else: - raise - if fk_col.shares_lineage(c) and ( - not only_synonyms or c.name == col.name - ): - omit.add(col) - break - - if clauses: - - def visit_binary(binary): - if binary.operator == operators.eq: - cols = util.column_set( - chain( - *[c.proxy_set for c in cset_no_text.difference(omit)] - ) - ) - if binary.left in cols and binary.right in cols: - for c in reversed(cset_no_text): - if c.shares_lineage(binary.right) and ( - not only_synonyms or c.name == binary.left.name - ): - omit.add(c) - break - - for clause in clauses: - if clause is not None: - visitors.traverse(clause, {}, {"binary": visit_binary}) - - return column_set.difference(omit) - - -def criterion_as_pairs( - expression, - consider_as_foreign_keys=None, - consider_as_referenced_keys=None, - any_operator=False, -): - """traverse an expression and locate binary criterion pairs.""" - - if consider_as_foreign_keys and consider_as_referenced_keys: - raise exc.ArgumentError( - "Can only specify one of " - "'consider_as_foreign_keys' or " - "'consider_as_referenced_keys'" - ) - - def col_is(a, b): - # return a is b - return a.compare(b) - - def visit_binary(binary): - if not any_operator and binary.operator is not operators.eq: - return - if not isinstance(binary.left, ColumnElement) or not isinstance( - binary.right, ColumnElement - ): - return - - if consider_as_foreign_keys: - if binary.left in consider_as_foreign_keys and ( - col_is(binary.right, binary.left) - or binary.right not in consider_as_foreign_keys - ): - pairs.append((binary.right, binary.left)) - elif binary.right in consider_as_foreign_keys and ( - col_is(binary.left, binary.right) - or binary.left not in consider_as_foreign_keys - ): - pairs.append((binary.left, binary.right)) - elif consider_as_referenced_keys: - if binary.left in consider_as_referenced_keys and ( - col_is(binary.right, binary.left) - or binary.right not in consider_as_referenced_keys - ): - pairs.append((binary.left, binary.right)) - elif binary.right in consider_as_referenced_keys and ( - col_is(binary.left, binary.right) - or binary.left not in consider_as_referenced_keys - ): - pairs.append((binary.right, binary.left)) - else: - if isinstance(binary.left, Column) and isinstance( - binary.right, Column - ): - if binary.left.references(binary.right): - pairs.append((binary.right, binary.left)) - elif binary.right.references(binary.left): - pairs.append((binary.left, binary.right)) - - pairs: List[Tuple[ColumnElement[Any], ColumnElement[Any]]] = [] - visitors.traverse(expression, {}, {"binary": visit_binary}) - return pairs - - -class ClauseAdapter(visitors.ReplacingExternalTraversal): - """Clones and modifies clauses based on column correspondence. - - E.g.:: - - table1 = Table('sometable', metadata, - Column('col1', Integer), - Column('col2', Integer) - ) - table2 = Table('someothertable', metadata, - Column('col1', Integer), - Column('col2', Integer) - ) - - condition = table1.c.col1 == table2.c.col1 - - make an alias of table1:: - - s = table1.alias('foo') - - calling ``ClauseAdapter(s).traverse(condition)`` converts - condition to read:: - - s.c.col1 == table2.c.col1 - - """ - - __slots__ = ( - "__traverse_options__", - "selectable", - "include_fn", - "exclude_fn", - "equivalents", - "adapt_on_names", - "adapt_from_selectables", - ) - - def __init__( - self, - selectable: Selectable, - equivalents: Optional[_EquivalentColumnMap] = None, - include_fn: Optional[Callable[[ClauseElement], bool]] = None, - exclude_fn: Optional[Callable[[ClauseElement], bool]] = None, - adapt_on_names: bool = False, - anonymize_labels: bool = False, - adapt_from_selectables: Optional[AbstractSet[FromClause]] = None, - ): - self.__traverse_options__ = { - "stop_on": [selectable], - "anonymize_labels": anonymize_labels, - } - self.selectable = selectable - self.include_fn = include_fn - self.exclude_fn = exclude_fn - self.equivalents = util.column_dict(equivalents or {}) - self.adapt_on_names = adapt_on_names - self.adapt_from_selectables = adapt_from_selectables - - if TYPE_CHECKING: - - @overload - def traverse(self, obj: Literal[None]) -> None: ... - - # note this specializes the ReplacingExternalTraversal.traverse() - # method to state - # that we will return the same kind of ExternalTraversal object as - # we were given. This is probably not 100% true, such as it's - # possible for us to swap out Alias for Table at the top level. - # Ideally there could be overloads specific to ColumnElement and - # FromClause but Mypy is not accepting those as compatible with - # the base ReplacingExternalTraversal - @overload - def traverse(self, obj: _ET) -> _ET: ... - - def traverse( - self, obj: Optional[ExternallyTraversible] - ) -> Optional[ExternallyTraversible]: ... - - def _corresponding_column( - self, col, require_embedded, _seen=util.EMPTY_SET - ): - newcol = self.selectable.corresponding_column( - col, require_embedded=require_embedded - ) - if newcol is None and col in self.equivalents and col not in _seen: - for equiv in self.equivalents[col]: - newcol = self._corresponding_column( - equiv, - require_embedded=require_embedded, - _seen=_seen.union([col]), - ) - if newcol is not None: - return newcol - - if ( - self.adapt_on_names - and newcol is None - and isinstance(col, NamedColumn) - ): - newcol = self.selectable.exported_columns.get(col.name) - return newcol - - @util.preload_module("sqlalchemy.sql.functions") - def replace( - self, col: _ET, _include_singleton_constants: bool = False - ) -> Optional[_ET]: - functions = util.preloaded.sql_functions - - # TODO: cython candidate - - if self.include_fn and not self.include_fn(col): # type: ignore - return None - elif self.exclude_fn and self.exclude_fn(col): # type: ignore - return None - - if isinstance(col, FromClause) and not isinstance( - col, functions.FunctionElement - ): - if self.selectable.is_derived_from(col): - if self.adapt_from_selectables: - for adp in self.adapt_from_selectables: - if adp.is_derived_from(col): - break - else: - return None - return self.selectable # type: ignore - elif isinstance(col, Alias) and isinstance( - col.element, TableClause - ): - # we are a SELECT statement and not derived from an alias of a - # table (which nonetheless may be a table our SELECT derives - # from), so return the alias to prevent further traversal - # or - # we are an alias of a table and we are not derived from an - # alias of a table (which nonetheless may be the same table - # as ours) so, same thing - return col # type: ignore - else: - # other cases where we are a selectable and the element - # is another join or selectable that contains a table which our - # selectable derives from, that we want to process - return None - - elif not isinstance(col, ColumnElement): - return None - elif not _include_singleton_constants and col._is_singleton_constant: - # dont swap out NULL, TRUE, FALSE for a label name - # in a SQL statement that's being rewritten, - # leave them as the constant. This is first noted in #6259, - # however the logic to check this moved here as of #7154 so that - # it is made specific to SQL rewriting and not all column - # correspondence - - return None - - if "adapt_column" in col._annotations: - col = col._annotations["adapt_column"] - - if TYPE_CHECKING: - assert isinstance(col, KeyedColumnElement) - - if self.adapt_from_selectables and col not in self.equivalents: - for adp in self.adapt_from_selectables: - if adp.c.corresponding_column(col, False) is not None: - break - else: - return None - - if TYPE_CHECKING: - assert isinstance(col, KeyedColumnElement) - - return self._corresponding_column( # type: ignore - col, require_embedded=True - ) - - -class _ColumnLookup(Protocol): - @overload - def __getitem__(self, key: None) -> None: ... - - @overload - def __getitem__(self, key: ColumnClause[Any]) -> ColumnClause[Any]: ... - - @overload - def __getitem__(self, key: ColumnElement[Any]) -> ColumnElement[Any]: ... - - @overload - def __getitem__(self, key: _ET) -> _ET: ... - - def __getitem__(self, key: Any) -> Any: ... - - -class ColumnAdapter(ClauseAdapter): - """Extends ClauseAdapter with extra utility functions. - - Key aspects of ColumnAdapter include: - - * Expressions that are adapted are stored in a persistent - .columns collection; so that an expression E adapted into - an expression E1, will return the same object E1 when adapted - a second time. This is important in particular for things like - Label objects that are anonymized, so that the ColumnAdapter can - be used to present a consistent "adapted" view of things. - - * Exclusion of items from the persistent collection based on - include/exclude rules, but also independent of hash identity. - This because "annotated" items all have the same hash identity as their - parent. - - * "wrapping" capability is added, so that the replacement of an expression - E can proceed through a series of adapters. This differs from the - visitor's "chaining" feature in that the resulting object is passed - through all replacing functions unconditionally, rather than stopping - at the first one that returns non-None. - - * An adapt_required option, used by eager loading to indicate that - We don't trust a result row column that is not translated. - This is to prevent a column from being interpreted as that - of the child row in a self-referential scenario, see - inheritance/test_basic.py->EagerTargetingTest.test_adapt_stringency - - """ - - __slots__ = ( - "columns", - "adapt_required", - "allow_label_resolve", - "_wrap", - "__weakref__", - ) - - columns: _ColumnLookup - - def __init__( - self, - selectable: Selectable, - equivalents: Optional[_EquivalentColumnMap] = None, - adapt_required: bool = False, - include_fn: Optional[Callable[[ClauseElement], bool]] = None, - exclude_fn: Optional[Callable[[ClauseElement], bool]] = None, - adapt_on_names: bool = False, - allow_label_resolve: bool = True, - anonymize_labels: bool = False, - adapt_from_selectables: Optional[AbstractSet[FromClause]] = None, - ): - super().__init__( - selectable, - equivalents, - include_fn=include_fn, - exclude_fn=exclude_fn, - adapt_on_names=adapt_on_names, - anonymize_labels=anonymize_labels, - adapt_from_selectables=adapt_from_selectables, - ) - - self.columns = util.WeakPopulateDict(self._locate_col) # type: ignore - if self.include_fn or self.exclude_fn: - self.columns = self._IncludeExcludeMapping(self, self.columns) - self.adapt_required = adapt_required - self.allow_label_resolve = allow_label_resolve - self._wrap = None - - class _IncludeExcludeMapping: - def __init__(self, parent, columns): - self.parent = parent - self.columns = columns - - def __getitem__(self, key): - if ( - self.parent.include_fn and not self.parent.include_fn(key) - ) or (self.parent.exclude_fn and self.parent.exclude_fn(key)): - if self.parent._wrap: - return self.parent._wrap.columns[key] - else: - return key - return self.columns[key] - - def wrap(self, adapter): - ac = copy.copy(self) - ac._wrap = adapter - ac.columns = util.WeakPopulateDict(ac._locate_col) # type: ignore - if ac.include_fn or ac.exclude_fn: - ac.columns = self._IncludeExcludeMapping(ac, ac.columns) - - return ac - - @overload - def traverse(self, obj: Literal[None]) -> None: ... - - @overload - def traverse(self, obj: _ET) -> _ET: ... - - def traverse( - self, obj: Optional[ExternallyTraversible] - ) -> Optional[ExternallyTraversible]: - return self.columns[obj] - - def chain(self, visitor: ExternalTraversal) -> ColumnAdapter: - assert isinstance(visitor, ColumnAdapter) - - return super().chain(visitor) - - if TYPE_CHECKING: - - @property - def visitor_iterator(self) -> Iterator[ColumnAdapter]: ... - - adapt_clause = traverse - adapt_list = ClauseAdapter.copy_and_process - - def adapt_check_present( - self, col: ColumnElement[Any] - ) -> Optional[ColumnElement[Any]]: - newcol = self.columns[col] - - if newcol is col and self._corresponding_column(col, True) is None: - return None - - return newcol - - def _locate_col( - self, col: ColumnElement[Any] - ) -> Optional[ColumnElement[Any]]: - # both replace and traverse() are overly complicated for what - # we are doing here and we would do better to have an inlined - # version that doesn't build up as much overhead. the issue is that - # sometimes the lookup does in fact have to adapt the insides of - # say a labeled scalar subquery. However, if the object is an - # Immutable, i.e. Column objects, we can skip the "clone" / - # "copy internals" part since those will be no-ops in any case. - # additionally we want to catch singleton objects null/true/false - # and make sure they are adapted as well here. - - if col._is_immutable: - for vis in self.visitor_iterator: - c = vis.replace(col, _include_singleton_constants=True) - if c is not None: - break - else: - c = col - else: - c = ClauseAdapter.traverse(self, col) - - if self._wrap: - c2 = self._wrap._locate_col(c) - if c2 is not None: - c = c2 - - if self.adapt_required and c is col: - return None - - # allow_label_resolve is consumed by one case for joined eager loading - # as part of its logic to prevent its own columns from being affected - # by .order_by(). Before full typing were applied to the ORM, this - # logic would set this attribute on the incoming object (which is - # typically a column, but we have a test for it being a non-column - # object) if no column were found. While this seemed to - # have no negative effects, this adjustment should only occur on the - # new column which is assumed to be local to an adapted selectable. - if c is not col: - c._allow_label_resolve = self.allow_label_resolve - - return c - - -def _offset_or_limit_clause( - element: _LimitOffsetType, - name: Optional[str] = None, - type_: Optional[_TypeEngineArgument[int]] = None, -) -> ColumnElement[int]: - """Convert the given value to an "offset or limit" clause. - - This handles incoming integers and converts to an expression; if - an expression is already given, it is passed through. - - """ - return coercions.expect( - roles.LimitOffsetRole, element, name=name, type_=type_ - ) - - -def _offset_or_limit_clause_asint_if_possible( - clause: _LimitOffsetType, -) -> _LimitOffsetType: - """Return the offset or limit clause as a simple integer if possible, - else return the clause. - - """ - if clause is None: - return None - if hasattr(clause, "_limit_offset_value"): - value = clause._limit_offset_value - return util.asint(value) - else: - return clause - - -def _make_slice( - limit_clause: _LimitOffsetType, - offset_clause: _LimitOffsetType, - start: int, - stop: int, -) -> Tuple[Optional[ColumnElement[int]], Optional[ColumnElement[int]]]: - """Compute LIMIT/OFFSET in terms of slice start/end""" - - # for calculated limit/offset, try to do the addition of - # values to offset in Python, however if a SQL clause is present - # then the addition has to be on the SQL side. - - # TODO: typing is finding a few gaps in here, see if they can be - # closed up - - if start is not None and stop is not None: - offset_clause = _offset_or_limit_clause_asint_if_possible( - offset_clause - ) - if offset_clause is None: - offset_clause = 0 - - if start != 0: - offset_clause = offset_clause + start # type: ignore - - if offset_clause == 0: - offset_clause = None - else: - assert offset_clause is not None - offset_clause = _offset_or_limit_clause(offset_clause) - - limit_clause = _offset_or_limit_clause(stop - start) - - elif start is None and stop is not None: - limit_clause = _offset_or_limit_clause(stop) - elif start is not None and stop is None: - offset_clause = _offset_or_limit_clause_asint_if_possible( - offset_clause - ) - if offset_clause is None: - offset_clause = 0 - - if start != 0: - offset_clause = offset_clause + start - - if offset_clause == 0: - offset_clause = None - else: - offset_clause = _offset_or_limit_clause(offset_clause) - - return limit_clause, offset_clause diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/sql/visitors.py b/venv/lib/python3.11/site-packages/sqlalchemy/sql/visitors.py deleted file mode 100644 index d1cd7a9..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/sql/visitors.py +++ /dev/null @@ -1,1165 +0,0 @@ -# sql/visitors.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -"""Visitor/traversal interface and library functions. - - -""" - -from __future__ import annotations - -from collections import deque -from enum import Enum -import itertools -import operator -import typing -from typing import Any -from typing import Callable -from typing import cast -from typing import ClassVar -from typing import Dict -from typing import Iterable -from typing import Iterator -from typing import List -from typing import Mapping -from typing import Optional -from typing import overload -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union - -from .. import exc -from .. import util -from ..util import langhelpers -from ..util._has_cy import HAS_CYEXTENSION -from ..util.typing import Literal -from ..util.typing import Protocol -from ..util.typing import Self - -if TYPE_CHECKING: - from .annotation import _AnnotationDict - from .elements import ColumnElement - -if typing.TYPE_CHECKING or not HAS_CYEXTENSION: - from ._py_util import prefix_anon_map as prefix_anon_map - from ._py_util import cache_anon_map as anon_map -else: - from sqlalchemy.cyextension.util import ( # noqa: F401,E501 - prefix_anon_map as prefix_anon_map, - ) - from sqlalchemy.cyextension.util import ( # noqa: F401,E501 - cache_anon_map as anon_map, - ) - - -__all__ = [ - "iterate", - "traverse_using", - "traverse", - "cloned_traverse", - "replacement_traverse", - "Visitable", - "ExternalTraversal", - "InternalTraversal", - "anon_map", -] - - -class _CompilerDispatchType(Protocol): - def __call__(_self, self: Visitable, visitor: Any, **kw: Any) -> Any: ... - - -class Visitable: - """Base class for visitable objects. - - :class:`.Visitable` is used to implement the SQL compiler dispatch - functions. Other forms of traversal such as for cache key generation - are implemented separately using the :class:`.HasTraverseInternals` - interface. - - .. versionchanged:: 2.0 The :class:`.Visitable` class was named - :class:`.Traversible` in the 1.4 series; the name is changed back - to :class:`.Visitable` in 2.0 which is what it was prior to 1.4. - - Both names remain importable in both 1.4 and 2.0 versions. - - """ - - __slots__ = () - - __visit_name__: str - - _original_compiler_dispatch: _CompilerDispatchType - - if typing.TYPE_CHECKING: - - def _compiler_dispatch(self, visitor: Any, **kw: Any) -> str: ... - - def __init_subclass__(cls) -> None: - if "__visit_name__" in cls.__dict__: - cls._generate_compiler_dispatch() - super().__init_subclass__() - - @classmethod - def _generate_compiler_dispatch(cls) -> None: - visit_name = cls.__visit_name__ - - if "_compiler_dispatch" in cls.__dict__: - # class has a fixed _compiler_dispatch() method. - # copy it to "original" so that we can get it back if - # sqlalchemy.ext.compiles overrides it. - cls._original_compiler_dispatch = cls._compiler_dispatch - return - - if not isinstance(visit_name, str): - raise exc.InvalidRequestError( - f"__visit_name__ on class {cls.__name__} must be a string " - "at the class level" - ) - - name = "visit_%s" % visit_name - getter = operator.attrgetter(name) - - def _compiler_dispatch( - self: Visitable, visitor: Any, **kw: Any - ) -> str: - """Look for an attribute named "visit_<visit_name>" on the - visitor, and call it with the same kw params. - - """ - try: - meth = getter(visitor) - except AttributeError as err: - return visitor.visit_unsupported_compilation(self, err, **kw) # type: ignore # noqa: E501 - else: - return meth(self, **kw) # type: ignore # noqa: E501 - - cls._compiler_dispatch = ( # type: ignore - cls._original_compiler_dispatch - ) = _compiler_dispatch - - def __class_getitem__(cls, key: Any) -> Any: - # allow generic classes in py3.9+ - return cls - - -class InternalTraversal(Enum): - r"""Defines visitor symbols used for internal traversal. - - The :class:`.InternalTraversal` class is used in two ways. One is that - it can serve as the superclass for an object that implements the - various visit methods of the class. The other is that the symbols - themselves of :class:`.InternalTraversal` are used within - the ``_traverse_internals`` collection. Such as, the :class:`.Case` - object defines ``_traverse_internals`` as :: - - class Case(ColumnElement[_T]): - _traverse_internals = [ - ("value", InternalTraversal.dp_clauseelement), - ("whens", InternalTraversal.dp_clauseelement_tuples), - ("else_", InternalTraversal.dp_clauseelement), - ] - - Above, the :class:`.Case` class indicates its internal state as the - attributes named ``value``, ``whens``, and ``else_``. They each - link to an :class:`.InternalTraversal` method which indicates the type - of datastructure to which each attribute refers. - - Using the ``_traverse_internals`` structure, objects of type - :class:`.InternalTraversible` will have the following methods automatically - implemented: - - * :meth:`.HasTraverseInternals.get_children` - - * :meth:`.HasTraverseInternals._copy_internals` - - * :meth:`.HasCacheKey._gen_cache_key` - - Subclasses can also implement these methods directly, particularly for the - :meth:`.HasTraverseInternals._copy_internals` method, when special steps - are needed. - - .. versionadded:: 1.4 - - """ - - dp_has_cache_key = "HC" - """Visit a :class:`.HasCacheKey` object.""" - - dp_has_cache_key_list = "HL" - """Visit a list of :class:`.HasCacheKey` objects.""" - - dp_clauseelement = "CE" - """Visit a :class:`_expression.ClauseElement` object.""" - - dp_fromclause_canonical_column_collection = "FC" - """Visit a :class:`_expression.FromClause` object in the context of the - ``columns`` attribute. - - The column collection is "canonical", meaning it is the originally - defined location of the :class:`.ColumnClause` objects. Right now - this means that the object being visited is a - :class:`_expression.TableClause` - or :class:`_schema.Table` object only. - - """ - - dp_clauseelement_tuples = "CTS" - """Visit a list of tuples which contain :class:`_expression.ClauseElement` - objects. - - """ - - dp_clauseelement_list = "CL" - """Visit a list of :class:`_expression.ClauseElement` objects. - - """ - - dp_clauseelement_tuple = "CT" - """Visit a tuple of :class:`_expression.ClauseElement` objects. - - """ - - dp_executable_options = "EO" - - dp_with_context_options = "WC" - - dp_fromclause_ordered_set = "CO" - """Visit an ordered set of :class:`_expression.FromClause` objects. """ - - dp_string = "S" - """Visit a plain string value. - - Examples include table and column names, bound parameter keys, special - keywords such as "UNION", "UNION ALL". - - The string value is considered to be significant for cache key - generation. - - """ - - dp_string_list = "SL" - """Visit a list of strings.""" - - dp_anon_name = "AN" - """Visit a potentially "anonymized" string value. - - The string value is considered to be significant for cache key - generation. - - """ - - dp_boolean = "B" - """Visit a boolean value. - - The boolean value is considered to be significant for cache key - generation. - - """ - - dp_operator = "O" - """Visit an operator. - - The operator is a function from the :mod:`sqlalchemy.sql.operators` - module. - - The operator value is considered to be significant for cache key - generation. - - """ - - dp_type = "T" - """Visit a :class:`.TypeEngine` object - - The type object is considered to be significant for cache key - generation. - - """ - - dp_plain_dict = "PD" - """Visit a dictionary with string keys. - - The keys of the dictionary should be strings, the values should - be immutable and hashable. The dictionary is considered to be - significant for cache key generation. - - """ - - dp_dialect_options = "DO" - """Visit a dialect options structure.""" - - dp_string_clauseelement_dict = "CD" - """Visit a dictionary of string keys to :class:`_expression.ClauseElement` - objects. - - """ - - dp_string_multi_dict = "MD" - """Visit a dictionary of string keys to values which may either be - plain immutable/hashable or :class:`.HasCacheKey` objects. - - """ - - dp_annotations_key = "AK" - """Visit the _annotations_cache_key element. - - This is a dictionary of additional information about a ClauseElement - that modifies its role. It should be included when comparing or caching - objects, however generating this key is relatively expensive. Visitors - should check the "_annotations" dict for non-None first before creating - this key. - - """ - - dp_plain_obj = "PO" - """Visit a plain python object. - - The value should be immutable and hashable, such as an integer. - The value is considered to be significant for cache key generation. - - """ - - dp_named_ddl_element = "DD" - """Visit a simple named DDL element. - - The current object used by this method is the :class:`.Sequence`. - - The object is only considered to be important for cache key generation - as far as its name, but not any other aspects of it. - - """ - - dp_prefix_sequence = "PS" - """Visit the sequence represented by :class:`_expression.HasPrefixes` - or :class:`_expression.HasSuffixes`. - - """ - - dp_table_hint_list = "TH" - """Visit the ``_hints`` collection of a :class:`_expression.Select` - object. - - """ - - dp_setup_join_tuple = "SJ" - - dp_memoized_select_entities = "ME" - - dp_statement_hint_list = "SH" - """Visit the ``_statement_hints`` collection of a - :class:`_expression.Select` - object. - - """ - - dp_unknown_structure = "UK" - """Visit an unknown structure. - - """ - - dp_dml_ordered_values = "DML_OV" - """Visit the values() ordered tuple list of an - :class:`_expression.Update` object.""" - - dp_dml_values = "DML_V" - """Visit the values() dictionary of a :class:`.ValuesBase` - (e.g. Insert or Update) object. - - """ - - dp_dml_multi_values = "DML_MV" - """Visit the values() multi-valued list of dictionaries of an - :class:`_expression.Insert` object. - - """ - - dp_propagate_attrs = "PA" - """Visit the propagate attrs dict. This hardcodes to the particular - elements we care about right now.""" - - """Symbols that follow are additional symbols that are useful in - caching applications. - - Traversals for :class:`_expression.ClauseElement` objects only need to use - those symbols present in :class:`.InternalTraversal`. However, for - additional caching use cases within the ORM, symbols dealing with the - :class:`.HasCacheKey` class are added here. - - """ - - dp_ignore = "IG" - """Specify an object that should be ignored entirely. - - This currently applies function call argument caching where some - arguments should not be considered to be part of a cache key. - - """ - - dp_inspectable = "IS" - """Visit an inspectable object where the return value is a - :class:`.HasCacheKey` object.""" - - dp_multi = "M" - """Visit an object that may be a :class:`.HasCacheKey` or may be a - plain hashable object.""" - - dp_multi_list = "MT" - """Visit a tuple containing elements that may be :class:`.HasCacheKey` or - may be a plain hashable object.""" - - dp_has_cache_key_tuples = "HT" - """Visit a list of tuples which contain :class:`.HasCacheKey` - objects. - - """ - - dp_inspectable_list = "IL" - """Visit a list of inspectable objects which upon inspection are - HasCacheKey objects.""" - - -_TraverseInternalsType = List[Tuple[str, InternalTraversal]] -"""a structure that defines how a HasTraverseInternals should be -traversed. - -This structure consists of a list of (attributename, internaltraversal) -tuples, where the "attributename" refers to the name of an attribute on an -instance of the HasTraverseInternals object, and "internaltraversal" refers -to an :class:`.InternalTraversal` enumeration symbol defining what kind -of data this attribute stores, which indicates to the traverser how it should -be handled. - -""" - - -class HasTraverseInternals: - """base for classes that have a "traverse internals" element, - which defines all kinds of ways of traversing the elements of an object. - - Compared to :class:`.Visitable`, which relies upon an external visitor to - define how the object is travered (i.e. the :class:`.SQLCompiler`), the - :class:`.HasTraverseInternals` interface allows classes to define their own - traversal, that is, what attributes are accessed and in what order. - - """ - - __slots__ = () - - _traverse_internals: _TraverseInternalsType - - _is_immutable: bool = False - - @util.preload_module("sqlalchemy.sql.traversals") - def get_children( - self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any - ) -> Iterable[HasTraverseInternals]: - r"""Return immediate child :class:`.visitors.HasTraverseInternals` - elements of this :class:`.visitors.HasTraverseInternals`. - - This is used for visit traversal. - - \**kw may contain flags that change the collection that is - returned, for example to return a subset of items in order to - cut down on larger traversals, or to return child items from a - different context (such as schema-level collections instead of - clause-level). - - """ - - traversals = util.preloaded.sql_traversals - - try: - traverse_internals = self._traverse_internals - except AttributeError: - # user-defined classes may not have a _traverse_internals - return [] - - dispatch = traversals._get_children.run_generated_dispatch - return itertools.chain.from_iterable( - meth(obj, **kw) - for attrname, obj, meth in dispatch( - self, traverse_internals, "_generated_get_children_traversal" - ) - if attrname not in omit_attrs and obj is not None - ) - - -class _InternalTraversalDispatchType(Protocol): - def __call__(s, self: object, visitor: HasTraversalDispatch) -> Any: ... - - -class HasTraversalDispatch: - r"""Define infrastructure for classes that perform internal traversals - - .. versionadded:: 2.0 - - """ - - __slots__ = () - - _dispatch_lookup: ClassVar[Dict[Union[InternalTraversal, str], str]] = {} - - def dispatch(self, visit_symbol: InternalTraversal) -> Callable[..., Any]: - """Given a method from :class:`.HasTraversalDispatch`, return the - corresponding method on a subclass. - - """ - name = _dispatch_lookup[visit_symbol] - return getattr(self, name, None) # type: ignore - - def run_generated_dispatch( - self, - target: object, - internal_dispatch: _TraverseInternalsType, - generate_dispatcher_name: str, - ) -> Any: - dispatcher: _InternalTraversalDispatchType - try: - dispatcher = target.__class__.__dict__[generate_dispatcher_name] - except KeyError: - # traversals.py -> _preconfigure_traversals() - # may be used to run these ahead of time, but - # is not enabled right now. - # this block will generate any remaining dispatchers. - dispatcher = self.generate_dispatch( - target.__class__, internal_dispatch, generate_dispatcher_name - ) - return dispatcher(target, self) - - def generate_dispatch( - self, - target_cls: Type[object], - internal_dispatch: _TraverseInternalsType, - generate_dispatcher_name: str, - ) -> _InternalTraversalDispatchType: - dispatcher = self._generate_dispatcher( - internal_dispatch, generate_dispatcher_name - ) - # assert isinstance(target_cls, type) - setattr(target_cls, generate_dispatcher_name, dispatcher) - return dispatcher - - def _generate_dispatcher( - self, internal_dispatch: _TraverseInternalsType, method_name: str - ) -> _InternalTraversalDispatchType: - names = [] - for attrname, visit_sym in internal_dispatch: - meth = self.dispatch(visit_sym) - if meth is not None: - visit_name = _dispatch_lookup[visit_sym] - names.append((attrname, visit_name)) - - code = ( - (" return [\n") - + ( - ", \n".join( - " (%r, self.%s, visitor.%s)" - % (attrname, attrname, visit_name) - for attrname, visit_name in names - ) - ) - + ("\n ]\n") - ) - meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n" - return cast( - _InternalTraversalDispatchType, - langhelpers._exec_code_in_env(meth_text, {}, method_name), - ) - - -ExtendedInternalTraversal = InternalTraversal - - -def _generate_traversal_dispatch() -> None: - lookup = _dispatch_lookup - - for sym in InternalTraversal: - key = sym.name - if key.startswith("dp_"): - visit_key = key.replace("dp_", "visit_") - sym_name = sym.value - assert sym_name not in lookup, sym_name - lookup[sym] = lookup[sym_name] = visit_key - - -_dispatch_lookup = HasTraversalDispatch._dispatch_lookup -_generate_traversal_dispatch() - - -class ExternallyTraversible(HasTraverseInternals, Visitable): - __slots__ = () - - _annotations: Mapping[Any, Any] = util.EMPTY_DICT - - if typing.TYPE_CHECKING: - - def _annotate(self, values: _AnnotationDict) -> Self: ... - - def get_children( - self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any - ) -> Iterable[ExternallyTraversible]: ... - - def _clone(self, **kw: Any) -> Self: - """clone this element""" - raise NotImplementedError() - - def _copy_internals( - self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any - ) -> None: - """Reassign internal elements to be clones of themselves. - - Called during a copy-and-traverse operation on newly - shallow-copied elements to create a deep copy. - - The given clone function should be used, which may be applying - additional transformations to the element (i.e. replacement - traversal, cloned traversal, annotations). - - """ - raise NotImplementedError() - - -_ET = TypeVar("_ET", bound=ExternallyTraversible) - -_CE = TypeVar("_CE", bound="ColumnElement[Any]") - -_TraverseCallableType = Callable[[_ET], None] - - -class _CloneCallableType(Protocol): - def __call__(self, element: _ET, **kw: Any) -> _ET: ... - - -class _TraverseTransformCallableType(Protocol[_ET]): - def __call__(self, element: _ET, **kw: Any) -> Optional[_ET]: ... - - -_ExtT = TypeVar("_ExtT", bound="ExternalTraversal") - - -class ExternalTraversal(util.MemoizedSlots): - """Base class for visitor objects which can traverse externally using - the :func:`.visitors.traverse` function. - - Direct usage of the :func:`.visitors.traverse` function is usually - preferred. - - """ - - __slots__ = ("_visitor_dict", "_next") - - __traverse_options__: Dict[str, Any] = {} - _next: Optional[ExternalTraversal] - - def traverse_single(self, obj: Visitable, **kw: Any) -> Any: - for v in self.visitor_iterator: - meth = getattr(v, "visit_%s" % obj.__visit_name__, None) - if meth: - return meth(obj, **kw) - - def iterate( - self, obj: Optional[ExternallyTraversible] - ) -> Iterator[ExternallyTraversible]: - """Traverse the given expression structure, returning an iterator - of all elements. - - """ - return iterate(obj, self.__traverse_options__) - - @overload - def traverse(self, obj: Literal[None]) -> None: ... - - @overload - def traverse( - self, obj: ExternallyTraversible - ) -> ExternallyTraversible: ... - - def traverse( - self, obj: Optional[ExternallyTraversible] - ) -> Optional[ExternallyTraversible]: - """Traverse and visit the given expression structure.""" - - return traverse(obj, self.__traverse_options__, self._visitor_dict) - - def _memoized_attr__visitor_dict( - self, - ) -> Dict[str, _TraverseCallableType[Any]]: - visitors = {} - - for name in dir(self): - if name.startswith("visit_"): - visitors[name[6:]] = getattr(self, name) - return visitors - - @property - def visitor_iterator(self) -> Iterator[ExternalTraversal]: - """Iterate through this visitor and each 'chained' visitor.""" - - v: Optional[ExternalTraversal] = self - while v: - yield v - v = getattr(v, "_next", None) - - def chain(self: _ExtT, visitor: ExternalTraversal) -> _ExtT: - """'Chain' an additional ExternalTraversal onto this ExternalTraversal - - The chained visitor will receive all visit events after this one. - - """ - tail = list(self.visitor_iterator)[-1] - tail._next = visitor - return self - - -class CloningExternalTraversal(ExternalTraversal): - """Base class for visitor objects which can traverse using - the :func:`.visitors.cloned_traverse` function. - - Direct usage of the :func:`.visitors.cloned_traverse` function is usually - preferred. - - - """ - - __slots__ = () - - def copy_and_process( - self, list_: List[ExternallyTraversible] - ) -> List[ExternallyTraversible]: - """Apply cloned traversal to the given list of elements, and return - the new list. - - """ - return [self.traverse(x) for x in list_] - - @overload - def traverse(self, obj: Literal[None]) -> None: ... - - @overload - def traverse( - self, obj: ExternallyTraversible - ) -> ExternallyTraversible: ... - - def traverse( - self, obj: Optional[ExternallyTraversible] - ) -> Optional[ExternallyTraversible]: - """Traverse and visit the given expression structure.""" - - return cloned_traverse( - obj, self.__traverse_options__, self._visitor_dict - ) - - -class ReplacingExternalTraversal(CloningExternalTraversal): - """Base class for visitor objects which can traverse using - the :func:`.visitors.replacement_traverse` function. - - Direct usage of the :func:`.visitors.replacement_traverse` function is - usually preferred. - - """ - - __slots__ = () - - def replace( - self, elem: ExternallyTraversible - ) -> Optional[ExternallyTraversible]: - """Receive pre-copied elements during a cloning traversal. - - If the method returns a new element, the element is used - instead of creating a simple copy of the element. Traversal - will halt on the newly returned element if it is re-encountered. - """ - return None - - @overload - def traverse(self, obj: Literal[None]) -> None: ... - - @overload - def traverse( - self, obj: ExternallyTraversible - ) -> ExternallyTraversible: ... - - def traverse( - self, obj: Optional[ExternallyTraversible] - ) -> Optional[ExternallyTraversible]: - """Traverse and visit the given expression structure.""" - - def replace( - element: ExternallyTraversible, - **kw: Any, - ) -> Optional[ExternallyTraversible]: - for v in self.visitor_iterator: - e = cast(ReplacingExternalTraversal, v).replace(element) - if e is not None: - return e - - return None - - return replacement_traverse(obj, self.__traverse_options__, replace) - - -# backwards compatibility -Traversible = Visitable - -ClauseVisitor = ExternalTraversal -CloningVisitor = CloningExternalTraversal -ReplacingCloningVisitor = ReplacingExternalTraversal - - -def iterate( - obj: Optional[ExternallyTraversible], - opts: Mapping[str, Any] = util.EMPTY_DICT, -) -> Iterator[ExternallyTraversible]: - r"""Traverse the given expression structure, returning an iterator. - - Traversal is configured to be breadth-first. - - The central API feature used by the :func:`.visitors.iterate` - function is the - :meth:`_expression.ClauseElement.get_children` method of - :class:`_expression.ClauseElement` objects. This method should return all - the :class:`_expression.ClauseElement` objects which are associated with a - particular :class:`_expression.ClauseElement` object. For example, a - :class:`.Case` structure will refer to a series of - :class:`_expression.ColumnElement` objects within its "whens" and "else\_" - member variables. - - :param obj: :class:`_expression.ClauseElement` structure to be traversed - - :param opts: dictionary of iteration options. This dictionary is usually - empty in modern usage. - - """ - if obj is None: - return - - yield obj - children = obj.get_children(**opts) - - if not children: - return - - stack = deque([children]) - while stack: - t_iterator = stack.popleft() - for t in t_iterator: - yield t - stack.append(t.get_children(**opts)) - - -@overload -def traverse_using( - iterator: Iterable[ExternallyTraversible], - obj: Literal[None], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> None: ... - - -@overload -def traverse_using( - iterator: Iterable[ExternallyTraversible], - obj: ExternallyTraversible, - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> ExternallyTraversible: ... - - -def traverse_using( - iterator: Iterable[ExternallyTraversible], - obj: Optional[ExternallyTraversible], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> Optional[ExternallyTraversible]: - """Visit the given expression structure using the given iterator of - objects. - - :func:`.visitors.traverse_using` is usually called internally as the result - of the :func:`.visitors.traverse` function. - - :param iterator: an iterable or sequence which will yield - :class:`_expression.ClauseElement` - structures; the iterator is assumed to be the - product of the :func:`.visitors.iterate` function. - - :param obj: the :class:`_expression.ClauseElement` - that was used as the target of the - :func:`.iterate` function. - - :param visitors: dictionary of visit functions. See :func:`.traverse` - for details on this dictionary. - - .. seealso:: - - :func:`.traverse` - - - """ - for target in iterator: - meth = visitors.get(target.__visit_name__, None) - if meth: - meth(target) - return obj - - -@overload -def traverse( - obj: Literal[None], - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> None: ... - - -@overload -def traverse( - obj: ExternallyTraversible, - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> ExternallyTraversible: ... - - -def traverse( - obj: Optional[ExternallyTraversible], - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> Optional[ExternallyTraversible]: - """Traverse and visit the given expression structure using the default - iterator. - - e.g.:: - - from sqlalchemy.sql import visitors - - stmt = select(some_table).where(some_table.c.foo == 'bar') - - def visit_bindparam(bind_param): - print("found bound value: %s" % bind_param.value) - - visitors.traverse(stmt, {}, {"bindparam": visit_bindparam}) - - The iteration of objects uses the :func:`.visitors.iterate` function, - which does a breadth-first traversal using a stack. - - :param obj: :class:`_expression.ClauseElement` structure to be traversed - - :param opts: dictionary of iteration options. This dictionary is usually - empty in modern usage. - - :param visitors: dictionary of visit functions. The dictionary should - have strings as keys, each of which would correspond to the - ``__visit_name__`` of a particular kind of SQL expression object, and - callable functions as values, each of which represents a visitor function - for that kind of object. - - """ - return traverse_using(iterate(obj, opts), obj, visitors) - - -@overload -def cloned_traverse( - obj: Literal[None], - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> None: ... - - -# a bit of controversy here, as the clone of the lead element -# *could* in theory replace with an entirely different kind of element. -# however this is really not how cloned_traverse is ever used internally -# at least. -@overload -def cloned_traverse( - obj: _ET, - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> _ET: ... - - -def cloned_traverse( - obj: Optional[ExternallyTraversible], - opts: Mapping[str, Any], - visitors: Mapping[str, _TraverseCallableType[Any]], -) -> Optional[ExternallyTraversible]: - """Clone the given expression structure, allowing modifications by - visitors for mutable objects. - - Traversal usage is the same as that of :func:`.visitors.traverse`. - The visitor functions present in the ``visitors`` dictionary may also - modify the internals of the given structure as the traversal proceeds. - - The :func:`.cloned_traverse` function does **not** provide objects that are - part of the :class:`.Immutable` interface to the visit methods (this - primarily includes :class:`.ColumnClause`, :class:`.Column`, - :class:`.TableClause` and :class:`.Table` objects). As this traversal is - only intended to allow in-place mutation of objects, :class:`.Immutable` - objects are skipped. The :meth:`.Immutable._clone` method is still called - on each object to allow for objects to replace themselves with a different - object based on a clone of their sub-internals (e.g. a - :class:`.ColumnClause` that clones its subquery to return a new - :class:`.ColumnClause`). - - .. versionchanged:: 2.0 The :func:`.cloned_traverse` function omits - objects that are part of the :class:`.Immutable` interface. - - The central API feature used by the :func:`.visitors.cloned_traverse` - and :func:`.visitors.replacement_traverse` functions, in addition to the - :meth:`_expression.ClauseElement.get_children` - function that is used to achieve - the iteration, is the :meth:`_expression.ClauseElement._copy_internals` - method. - For a :class:`_expression.ClauseElement` - structure to support cloning and replacement - traversals correctly, it needs to be able to pass a cloning function into - its internal members in order to make copies of them. - - .. seealso:: - - :func:`.visitors.traverse` - - :func:`.visitors.replacement_traverse` - - """ - - cloned: Dict[int, ExternallyTraversible] = {} - stop_on = set(opts.get("stop_on", [])) - - def deferred_copy_internals( - obj: ExternallyTraversible, - ) -> ExternallyTraversible: - return cloned_traverse(obj, opts, visitors) - - def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible: - if elem in stop_on: - return elem - else: - if id(elem) not in cloned: - if "replace" in kw: - newelem = cast( - Optional[ExternallyTraversible], kw["replace"](elem) - ) - if newelem is not None: - cloned[id(elem)] = newelem - return newelem - - # the _clone method for immutable normally returns "self". - # however, the method is still allowed to return a - # different object altogether; ColumnClause._clone() will - # based on options clone the subquery to which it is associated - # and return the new corresponding column. - cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw) - newelem._copy_internals(clone=clone, **kw) - - # however, visit methods which are tasked with in-place - # mutation of the object should not get access to the immutable - # object. - if not elem._is_immutable: - meth = visitors.get(newelem.__visit_name__, None) - if meth: - meth(newelem) - return cloned[id(elem)] - - if obj is not None: - obj = clone( - obj, deferred_copy_internals=deferred_copy_internals, **opts - ) - clone = None # type: ignore[assignment] # remove gc cycles - return obj - - -@overload -def replacement_traverse( - obj: Literal[None], - opts: Mapping[str, Any], - replace: _TraverseTransformCallableType[Any], -) -> None: ... - - -@overload -def replacement_traverse( - obj: _CE, - opts: Mapping[str, Any], - replace: _TraverseTransformCallableType[Any], -) -> _CE: ... - - -@overload -def replacement_traverse( - obj: ExternallyTraversible, - opts: Mapping[str, Any], - replace: _TraverseTransformCallableType[Any], -) -> ExternallyTraversible: ... - - -def replacement_traverse( - obj: Optional[ExternallyTraversible], - opts: Mapping[str, Any], - replace: _TraverseTransformCallableType[Any], -) -> Optional[ExternallyTraversible]: - """Clone the given expression structure, allowing element - replacement by a given replacement function. - - This function is very similar to the :func:`.visitors.cloned_traverse` - function, except instead of being passed a dictionary of visitors, all - elements are unconditionally passed into the given replace function. - The replace function then has the option to return an entirely new object - which will replace the one given. If it returns ``None``, then the object - is kept in place. - - The difference in usage between :func:`.visitors.cloned_traverse` and - :func:`.visitors.replacement_traverse` is that in the former case, an - already-cloned object is passed to the visitor function, and the visitor - function can then manipulate the internal state of the object. - In the case of the latter, the visitor function should only return an - entirely different object, or do nothing. - - The use case for :func:`.visitors.replacement_traverse` is that of - replacing a FROM clause inside of a SQL structure with a different one, - as is a common use case within the ORM. - - """ - - cloned = {} - stop_on = {id(x) for x in opts.get("stop_on", [])} - - def deferred_copy_internals( - obj: ExternallyTraversible, - ) -> ExternallyTraversible: - return replacement_traverse(obj, opts, replace) - - def clone(elem: ExternallyTraversible, **kw: Any) -> ExternallyTraversible: - if ( - id(elem) in stop_on - or "no_replacement_traverse" in elem._annotations - ): - return elem - else: - newelem = replace(elem) - if newelem is not None: - stop_on.add(id(newelem)) - return newelem # type: ignore - else: - # base "already seen" on id(), not hash, so that we don't - # replace an Annotated element with its non-annotated one, and - # vice versa - id_elem = id(elem) - if id_elem not in cloned: - if "replace" in kw: - newelem = kw["replace"](elem) - if newelem is not None: - cloned[id_elem] = newelem - return newelem # type: ignore - - cloned[id_elem] = newelem = elem._clone(**kw) - newelem._copy_internals(clone=clone, **kw) - return cloned[id_elem] # type: ignore - - if obj is not None: - obj = clone( - obj, deferred_copy_internals=deferred_copy_internals, **opts - ) - clone = None # type: ignore[assignment] # remove gc cycles - return obj |