1# sql/_typing.py 
    2# Copyright (C) 2022-2025 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: https://www.opensource.org/licenses/mit-license.php 
    7 
    8from __future__ import annotations 
    9 
    10import operator 
    11from typing import Any 
    12from typing import Callable 
    13from typing import Dict 
    14from typing import Generic 
    15from typing import Iterable 
    16from typing import Literal 
    17from typing import Mapping 
    18from typing import NoReturn 
    19from typing import Optional 
    20from typing import overload 
    21from typing import Protocol 
    22from typing import Set 
    23from typing import Type 
    24from typing import TYPE_CHECKING 
    25from typing import TypeAlias 
    26from typing import TypeVar 
    27from typing import Union 
    28 
    29from . import roles 
    30from .. import exc 
    31from .. import util 
    32from ..inspection import Inspectable 
    33from ..util.typing import TupleAny 
    34from ..util.typing import TypeVarTuple 
    35from ..util.typing import Unpack 
    36 
    37if TYPE_CHECKING: 
    38    from datetime import date 
    39    from datetime import datetime 
    40    from datetime import time 
    41    from datetime import timedelta 
    42    from decimal import Decimal 
    43    from typing import TypeGuard 
    44    from uuid import UUID 
    45 
    46    from .base import Executable 
    47    from .compiler import Compiled 
    48    from .compiler import DDLCompiler 
    49    from .compiler import SQLCompiler 
    50    from .dml import UpdateBase 
    51    from .dml import ValuesBase 
    52    from .elements import ClauseElement 
    53    from .elements import ColumnElement 
    54    from .elements import KeyedColumnElement 
    55    from .elements import quoted_name 
    56    from .elements import SQLCoreOperations 
    57    from .elements import TextClause 
    58    from .lambdas import LambdaElement 
    59    from .roles import FromClauseRole 
    60    from .schema import Column 
    61    from .selectable import Alias 
    62    from .selectable import CompoundSelect 
    63    from .selectable import CTE 
    64    from .selectable import FromClause 
    65    from .selectable import Join 
    66    from .selectable import NamedFromClause 
    67    from .selectable import ReturnsRows 
    68    from .selectable import Select 
    69    from .selectable import Selectable 
    70    from .selectable import SelectBase 
    71    from .selectable import Subquery 
    72    from .selectable import TableClause 
    73    from .sqltypes import TableValueType 
    74    from .sqltypes import TupleType 
    75    from .type_api import TypeEngine 
    76    from ..engine import Connection 
    77    from ..engine import Dialect 
    78    from ..engine import Engine 
    79    from ..engine.mock import MockConnection 
    80 
    81_T = TypeVar("_T", bound=Any) 
    82_T_co = TypeVar("_T_co", bound=Any, covariant=True) 
    83_Ts = TypeVarTuple("_Ts") 
    84 
    85 
    86_CE = TypeVar("_CE", bound="ColumnElement[Any]") 
    87 
    88_CLE = TypeVar("_CLE", bound="ClauseElement") 
    89 
    90 
    91class _HasClauseElement(Protocol, Generic[_T_co]): 
    92    """indicates a class that has a __clause_element__() method""" 
    93 
    94    def __clause_element__(self) -> roles.ExpressionElementRole[_T_co]: ... 
    95 
    96 
    97class _CoreAdapterProto(Protocol): 
    98    """protocol for the ClauseAdapter/ColumnAdapter.traverse() method.""" 
    99 
    100    def __call__(self, obj: _CE) -> _CE: ... 
    101 
    102 
    103class _HasDialect(Protocol): 
    104    """protocol for Engine/Connection-like objects that have dialect 
    105    attribute. 
    106    """ 
    107 
    108    @property 
    109    def dialect(self) -> Dialect: ... 
    110 
    111 
    112# match column types that are not ORM entities 
    113_NOT_ENTITY = TypeVar( 
    114    "_NOT_ENTITY", 
    115    int, 
    116    str, 
    117    bool, 
    118    "datetime", 
    119    "date", 
    120    "time", 
    121    "timedelta", 
    122    "UUID", 
    123    float, 
    124    "Decimal", 
    125) 
    126 
    127_StarOrOne = Literal["*", 1] 
    128 
    129_MAYBE_ENTITY = TypeVar( 
    130    "_MAYBE_ENTITY", 
    131    roles.ColumnsClauseRole, 
    132    _StarOrOne, 
    133    Type[Any], 
    134    Inspectable[_HasClauseElement[Any]], 
    135    _HasClauseElement[Any], 
    136) 
    137 
    138 
    139# convention: 
    140# XYZArgument - something that the end user is passing to a public API method 
    141# XYZElement - the internal representation that we use for the thing. 
    142# the coercions system is responsible for converting from XYZArgument to 
    143# XYZElement. 
    144 
    145_TextCoercedExpressionArgument = Union[ 
    146    str, 
    147    "TextClause", 
    148    "ColumnElement[_T]", 
    149    _HasClauseElement[_T], 
    150    roles.ExpressionElementRole[_T], 
    151] 
    152 
    153_ColumnsClauseArgument = Union[ 
    154    roles.TypedColumnsClauseRole[_T], 
    155    roles.ColumnsClauseRole, 
    156    "SQLCoreOperations[_T]", 
    157    _StarOrOne, 
    158    Type[_T], 
    159    Inspectable[_HasClauseElement[_T]], 
    160    _HasClauseElement[_T], 
    161] 
    162"""open-ended SELECT columns clause argument. 
    163 
    164Includes column expressions, tables, ORM mapped entities, a few literal values. 
    165 
    166This type is used for lists of columns  / entities to be returned in result 
    167sets; select(...), insert().returning(...), etc. 
    168 
    169 
    170""" 
    171 
    172_TypedColumnClauseArgument = Union[ 
    173    roles.TypedColumnsClauseRole[_T], 
    174    "SQLCoreOperations[_T]", 
    175    Type[_T], 
    176] 
    177 
    178_T0 = TypeVar("_T0", bound=Any) 
    179_T1 = TypeVar("_T1", bound=Any) 
    180_T2 = TypeVar("_T2", bound=Any) 
    181_T3 = TypeVar("_T3", bound=Any) 
    182_T4 = TypeVar("_T4", bound=Any) 
    183_T5 = TypeVar("_T5", bound=Any) 
    184_T6 = TypeVar("_T6", bound=Any) 
    185_T7 = TypeVar("_T7", bound=Any) 
    186_T8 = TypeVar("_T8", bound=Any) 
    187_T9 = TypeVar("_T9", bound=Any) 
    188 
    189 
    190_ColumnExpressionArgument = Union[ 
    191    "ColumnElement[_T]", 
    192    _HasClauseElement[_T], 
    193    "SQLCoreOperations[_T]", 
    194    roles.ExpressionElementRole[_T], 
    195    roles.TypedColumnsClauseRole[_T], 
    196    Callable[[], "ColumnElement[_T]"], 
    197    "LambdaElement", 
    198] 
    199"See docs in public alias ColumnExpressionArgument." 
    200 
    201ColumnExpressionArgument: TypeAlias = _ColumnExpressionArgument[_T] 
    202"""Narrower "column expression" argument. 
    203 
    204This type is used for all the other "column" kinds of expressions that 
    205typically represent a single SQL column expression, not a set of columns the 
    206way a table or ORM entity does. 
    207 
    208This includes ColumnElement, or ORM-mapped attributes that will have a 
    209``__clause_element__()`` method, it also has the ExpressionElementRole 
    210overall which brings in the TextClause object also. 
    211 
    212.. versionadded:: 2.0.13 
    213 
    214""" 
    215 
    216_ColumnExpressionOrLiteralArgument = Union[Any, _ColumnExpressionArgument[_T]] 
    217 
    218_ColumnExpressionOrStrLabelArgument = Union[str, _ColumnExpressionArgument[_T]] 
    219 
    220_ByArgument = Union[ 
    221    Iterable[_ColumnExpressionOrStrLabelArgument[Any]], 
    222    _ColumnExpressionOrStrLabelArgument[Any], 
    223] 
    224"""Used for keyword-based ``order_by`` and ``partition_by`` parameters.""" 
    225 
    226 
    227_InfoType = Dict[Any, Any] 
    228"""the .info dictionary accepted and used throughout Core /ORM""" 
    229 
    230_FromClauseArgument = Union[ 
    231    roles.FromClauseRole, 
    232    Type[Any], 
    233    Inspectable[_HasClauseElement[Any]], 
    234    _HasClauseElement[Any], 
    235] 
    236"""A FROM clause, like we would send to select().select_from(). 
    237 
    238Also accommodates ORM entities and related constructs. 
    239 
    240""" 
    241 
    242_JoinTargetArgument = Union[_FromClauseArgument, roles.JoinTargetRole] 
    243"""target for join() builds on _FromClauseArgument to include additional 
    244join target roles such as those which come from the ORM. 
    245 
    246""" 
    247 
    248_OnClauseArgument = Union[_ColumnExpressionArgument[Any], roles.OnClauseRole] 
    249"""target for an ON clause, includes additional roles such as those which 
    250come from the ORM. 
    251 
    252""" 
    253 
    254_SelectStatementForCompoundArgument = Union[ 
    255    "Select[Unpack[_Ts]]", 
    256    "CompoundSelect[Unpack[_Ts]]", 
    257    roles.CompoundElementRole, 
    258] 
    259"""SELECT statement acceptable by ``union()`` and other SQL set operations""" 
    260 
    261_DMLColumnArgument = Union[ 
    262    str, 
    263    _HasClauseElement[Any], 
    264    roles.DMLColumnRole, 
    265    "SQLCoreOperations[Any]", 
    266] 
    267"""A DML column expression.  This is a "key" inside of insert().values(), 
    268update().values(), and related. 
    269 
    270These are usually strings or SQL table columns. 
    271 
    272There's also edge cases like JSON expression assignment, which we would want 
    273the DMLColumnRole to be able to accommodate. 
    274 
    275""" 
    276 
    277_DMLOnlyColumnArgument = Union[ 
    278    _HasClauseElement[_T], 
    279    roles.DMLColumnRole, 
    280    "SQLCoreOperations[_T]", 
    281] 
    282 
    283 
    284_DMLKey = TypeVar("_DMLKey", bound=_DMLColumnArgument) 
    285_DMLColumnKeyMapping = Mapping[_DMLKey, Any] 
    286 
    287 
    288_DDLColumnArgument = Union[str, "Column[Any]", roles.DDLConstraintColumnRole] 
    289"""DDL column. 
    290 
    291used for :class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`, etc. 
    292 
    293""" 
    294 
    295_DDLColumnReferenceArgument = _DDLColumnArgument 
    296 
    297_DMLTableArgument = Union[ 
    298    "TableClause", 
    299    "Join", 
    300    "Alias", 
    301    "CTE", 
    302    Type[Any], 
    303    Inspectable[_HasClauseElement[Any]], 
    304    _HasClauseElement[Any], 
    305] 
    306 
    307_PropagateAttrsType = util.immutabledict[str, Any] 
    308 
    309_TypeEngineArgument = Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"] 
    310 
    311_EquivalentColumnMap = Dict["ColumnElement[Any]", Set["ColumnElement[Any]"]] 
    312 
    313_LimitOffsetType = Union[int, _ColumnExpressionArgument[int], None] 
    314 
    315_AutoIncrementType = Union[bool, Literal["auto", "ignore_fk"]] 
    316 
    317_CreateDropBind = Union["Engine", "Connection", "MockConnection"] 
    318 
    319if TYPE_CHECKING: 
    320 
    321    def is_sql_compiler(c: Compiled) -> TypeGuard[SQLCompiler]: ... 
    322 
    323    def is_ddl_compiler(c: Compiled) -> TypeGuard[DDLCompiler]: ... 
    324 
    325    def is_named_from_clause( 
    326        t: FromClauseRole, 
    327    ) -> TypeGuard[NamedFromClause]: ... 
    328 
    329    def is_column_element( 
    330        c: ClauseElement, 
    331    ) -> TypeGuard[ColumnElement[Any]]: ... 
    332 
    333    def is_keyed_column_element( 
    334        c: ClauseElement, 
    335    ) -> TypeGuard[KeyedColumnElement[Any]]: ... 
    336 
    337    def is_text_clause(c: ClauseElement) -> TypeGuard[TextClause]: ... 
    338 
    339    def is_from_clause(c: ClauseElement) -> TypeGuard[FromClause]: ... 
    340 
    341    def is_tuple_type(t: TypeEngine[Any]) -> TypeGuard[TupleType]: ... 
    342 
    343    def is_table_value_type( 
    344        t: TypeEngine[Any], 
    345    ) -> TypeGuard[TableValueType]: ... 
    346 
    347    def is_selectable(t: Any) -> TypeGuard[Selectable]: ... 
    348 
    349    def is_select_base( 
    350        t: Union[Executable, ReturnsRows], 
    351    ) -> TypeGuard[SelectBase]: ... 
    352 
    353    def is_select_statement( 
    354        t: Union[Executable, ReturnsRows], 
    355    ) -> TypeGuard[Select[Unpack[TupleAny]]]: ... 
    356 
    357    def is_table(t: FromClause) -> TypeGuard[TableClause]: ... 
    358 
    359    def is_subquery(t: FromClause) -> TypeGuard[Subquery]: ... 
    360 
    361    def is_dml(c: ClauseElement) -> TypeGuard[UpdateBase]: ... 
    362 
    363else: 
    364    is_sql_compiler = operator.attrgetter("is_sql") 
    365    is_ddl_compiler = operator.attrgetter("is_ddl") 
    366    is_named_from_clause = operator.attrgetter("named_with_column") 
    367    is_column_element = operator.attrgetter("_is_column_element") 
    368    is_keyed_column_element = operator.attrgetter("_is_keyed_column_element") 
    369    is_text_clause = operator.attrgetter("_is_text_clause") 
    370    is_from_clause = operator.attrgetter("_is_from_clause") 
    371    is_tuple_type = operator.attrgetter("_is_tuple_type") 
    372    is_table_value_type = operator.attrgetter("_is_table_value") 
    373    is_selectable = operator.attrgetter("is_selectable") 
    374    is_select_base = operator.attrgetter("_is_select_base") 
    375    is_select_statement = operator.attrgetter("_is_select_statement") 
    376    is_table = operator.attrgetter("_is_table") 
    377    is_subquery = operator.attrgetter("_is_subquery") 
    378    is_dml = operator.attrgetter("is_dml") 
    379 
    380 
    381def has_schema_attr(t: FromClauseRole) -> TypeGuard[TableClause]: 
    382    return hasattr(t, "schema") 
    383 
    384 
    385def is_quoted_name(s: str) -> TypeGuard[quoted_name]: 
    386    return hasattr(s, "quote") 
    387 
    388 
    389def is_has_clause_element(s: object) -> TypeGuard[_HasClauseElement[Any]]: 
    390    return hasattr(s, "__clause_element__") 
    391 
    392 
    393def is_insert_update(c: ClauseElement) -> TypeGuard[ValuesBase]: 
    394    return c.is_dml and (c.is_insert or c.is_update)  # type: ignore 
    395 
    396 
    397def _no_kw() -> exc.ArgumentError: 
    398    return exc.ArgumentError( 
    399        "Additional keyword arguments are not accepted by this " 
    400        "function/method.  The presence of **kw is for pep-484 typing purposes" 
    401    ) 
    402 
    403 
    404def _unexpected_kw(methname: str, kw: Dict[str, Any]) -> NoReturn: 
    405    k = list(kw)[0] 
    406    raise TypeError(f"{methname} got an unexpected keyword argument '{k}'") 
    407 
    408 
    409@overload 
    410def Nullable( 
    411    val: "SQLCoreOperations[_T]", 
    412) -> "SQLCoreOperations[Optional[_T]]": ... 
    413 
    414 
    415@overload 
    416def Nullable( 
    417    val: roles.ExpressionElementRole[_T], 
    418) -> roles.ExpressionElementRole[Optional[_T]]: ... 
    419 
    420 
    421@overload 
    422def Nullable(val: Type[_T]) -> Type[Optional[_T]]: ... 
    423 
    424 
    425def Nullable( 
    426    val: _TypedColumnClauseArgument[_T], 
    427) -> _TypedColumnClauseArgument[Optional[_T]]: 
    428    """Types a column or ORM class as nullable. 
    429 
    430    This can be used in select and other contexts to express that the value of 
    431    a column can be null, for example due to an outer join:: 
    432 
    433        stmt1 = select(A, Nullable(B)).outerjoin(A.bs) 
    434        stmt2 = select(A.data, Nullable(B.data)).outerjoin(A.bs) 
    435 
    436    At runtime this method returns the input unchanged. 
    437 
    438    .. versionadded:: 2.0.20 
    439    """ 
    440    return val 
    441 
    442 
    443@overload 
    444def NotNullable( 
    445    val: "SQLCoreOperations[Optional[_T]]", 
    446) -> "SQLCoreOperations[_T]": ... 
    447 
    448 
    449@overload 
    450def NotNullable( 
    451    val: roles.ExpressionElementRole[Optional[_T]], 
    452) -> roles.ExpressionElementRole[_T]: ... 
    453 
    454 
    455@overload 
    456def NotNullable(val: Type[Optional[_T]]) -> Type[_T]: ... 
    457 
    458 
    459@overload 
    460def NotNullable(val: Optional[Type[_T]]) -> Type[_T]: ... 
    461 
    462 
    463def NotNullable( 
    464    val: Union[_TypedColumnClauseArgument[Optional[_T]], Optional[Type[_T]]], 
    465) -> _TypedColumnClauseArgument[_T]: 
    466    """Types a column or ORM class as not nullable. 
    467 
    468    This can be used in select and other contexts to express that the value of 
    469    a column cannot be null, for example due to a where condition on a 
    470    nullable column:: 
    471 
    472        stmt = select(NotNullable(A.value)).where(A.value.is_not(None)) 
    473 
    474    At runtime this method returns the input unchanged. 
    475 
    476    .. versionadded:: 2.0.20 
    477    """ 
    478    return val  # type: ignore