1# engine/reflection.py 
    2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: https://www.opensource.org/licenses/mit-license.php 
    7 
    8"""Provides an abstraction for obtaining database schema information. 
    9 
    10Usage Notes: 
    11 
    12Here are some general conventions when accessing the low level inspector 
    13methods such as get_table_names, get_columns, etc. 
    14 
    151. Inspector methods return lists of dicts in most cases for the following 
    16   reasons: 
    17 
    18   * They're both standard types that can be serialized. 
    19   * Using a dict instead of a tuple allows easy expansion of attributes. 
    20   * Using a list for the outer structure maintains order and is easy to work 
    21     with (e.g. list comprehension [d['name'] for d in cols]). 
    22 
    232. Records that contain a name, such as the column name in a column record 
    24   use the key 'name'. So for most return values, each record will have a 
    25   'name' attribute.. 
    26""" 
    27from __future__ import annotations 
    28 
    29import contextlib 
    30from dataclasses import dataclass 
    31from enum import auto 
    32from enum import Flag 
    33from enum import unique 
    34from typing import Any 
    35from typing import Callable 
    36from typing import Collection 
    37from typing import Dict 
    38from typing import final 
    39from typing import Generator 
    40from typing import Iterable 
    41from typing import List 
    42from typing import Optional 
    43from typing import Sequence 
    44from typing import Set 
    45from typing import Tuple 
    46from typing import TYPE_CHECKING 
    47from typing import TypeVar 
    48from typing import Union 
    49 
    50from .base import Connection 
    51from .base import Engine 
    52from .. import exc 
    53from .. import inspection 
    54from .. import sql 
    55from .. import util 
    56from ..sql import operators 
    57from ..sql import schema as sa_schema 
    58from ..sql.cache_key import _ad_hoc_cache_key_from_args 
    59from ..sql.elements import quoted_name 
    60from ..sql.elements import TextClause 
    61from ..sql.type_api import TypeEngine 
    62from ..sql.visitors import InternalTraversal 
    63from ..util import topological 
    64 
    65if TYPE_CHECKING: 
    66    from .interfaces import Dialect 
    67    from .interfaces import ReflectedCheckConstraint 
    68    from .interfaces import ReflectedColumn 
    69    from .interfaces import ReflectedForeignKeyConstraint 
    70    from .interfaces import ReflectedIndex 
    71    from .interfaces import ReflectedPrimaryKeyConstraint 
    72    from .interfaces import ReflectedTableComment 
    73    from .interfaces import ReflectedUniqueConstraint 
    74    from .interfaces import TableKey 
    75 
    76_R = TypeVar("_R") 
    77 
    78 
    79@util.decorator 
    80def cache( 
    81    fn: Callable[..., _R], 
    82    self: Dialect, 
    83    con: Connection, 
    84    *args: Any, 
    85    **kw: Any, 
    86) -> _R: 
    87    info_cache = kw.get("info_cache", None) 
    88    if info_cache is None: 
    89        return fn(self, con, *args, **kw) 
    90    exclude = {"info_cache", "unreflectable"} 
    91    key = ( 
    92        fn.__name__, 
    93        tuple( 
    94            (str(a), a.quote) if isinstance(a, quoted_name) else a 
    95            for a in args 
    96            if isinstance(a, str) 
    97        ), 
    98        tuple( 
    99            (k, (str(v), v.quote) if isinstance(v, quoted_name) else v) 
    100            for k, v in kw.items() 
    101            if k not in exclude 
    102        ), 
    103    ) 
    104    ret: _R = info_cache.get(key) 
    105    if ret is None: 
    106        ret = fn(self, con, *args, **kw) 
    107        info_cache[key] = ret 
    108    return ret 
    109 
    110 
    111def flexi_cache( 
    112    *traverse_args: Tuple[str, InternalTraversal] 
    113) -> Callable[[Callable[..., _R]], Callable[..., _R]]: 
    114    @util.decorator 
    115    def go( 
    116        fn: Callable[..., _R], 
    117        self: Dialect, 
    118        con: Connection, 
    119        *args: Any, 
    120        **kw: Any, 
    121    ) -> _R: 
    122        info_cache = kw.get("info_cache", None) 
    123        if info_cache is None: 
    124            return fn(self, con, *args, **kw) 
    125        key = _ad_hoc_cache_key_from_args((fn.__name__,), traverse_args, args) 
    126        ret: _R = info_cache.get(key) 
    127        if ret is None: 
    128            ret = fn(self, con, *args, **kw) 
    129            info_cache[key] = ret 
    130        return ret 
    131 
    132    return go 
    133 
    134 
    135@unique 
    136class ObjectKind(Flag): 
    137    """Enumerator that indicates which kind of object to return when calling 
    138    the ``get_multi`` methods. 
    139 
    140    This is a Flag enum, so custom combinations can be passed. For example, 
    141    to reflect tables and plain views ``ObjectKind.TABLE | ObjectKind.VIEW`` 
    142    may be used. 
    143 
    144    .. note:: 
    145      Not all dialect may support all kind of object. If a dialect does 
    146      not support a particular object an empty dict is returned. 
    147      In case a dialect supports an object, but the requested method 
    148      is not applicable for the specified kind the default value 
    149      will be returned for each reflected object. For example reflecting 
    150      check constraints of view return a dict with all the views with 
    151      empty lists as values. 
    152    """ 
    153 
    154    TABLE = auto() 
    155    "Reflect table objects" 
    156    VIEW = auto() 
    157    "Reflect plain view objects" 
    158    MATERIALIZED_VIEW = auto() 
    159    "Reflect materialized view object" 
    160 
    161    ANY_VIEW = VIEW | MATERIALIZED_VIEW 
    162    "Reflect any kind of view objects" 
    163    ANY = TABLE | VIEW | MATERIALIZED_VIEW 
    164    "Reflect all type of objects" 
    165 
    166 
    167@unique 
    168class ObjectScope(Flag): 
    169    """Enumerator that indicates which scope to use when calling 
    170    the ``get_multi`` methods. 
    171    """ 
    172 
    173    DEFAULT = auto() 
    174    "Include default scope" 
    175    TEMPORARY = auto() 
    176    "Include only temp scope" 
    177    ANY = DEFAULT | TEMPORARY 
    178    "Include both default and temp scope" 
    179 
    180 
    181@inspection._self_inspects 
    182class Inspector(inspection.Inspectable["Inspector"]): 
    183    """Performs database schema inspection. 
    184 
    185    The Inspector acts as a proxy to the reflection methods of the 
    186    :class:`~sqlalchemy.engine.interfaces.Dialect`, providing a 
    187    consistent interface as well as caching support for previously 
    188    fetched metadata. 
    189 
    190    A :class:`_reflection.Inspector` object is usually created via the 
    191    :func:`_sa.inspect` function, which may be passed an 
    192    :class:`_engine.Engine` 
    193    or a :class:`_engine.Connection`:: 
    194 
    195        from sqlalchemy import inspect, create_engine 
    196 
    197        engine = create_engine("...") 
    198        insp = inspect(engine) 
    199 
    200    Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` associated 
    201    with the engine may opt to return an :class:`_reflection.Inspector` 
    202    subclass that 
    203    provides additional methods specific to the dialect's target database. 
    204 
    205    """ 
    206 
    207    bind: Union[Engine, Connection] 
    208    engine: Engine 
    209    _op_context_requires_connect: bool 
    210    dialect: Dialect 
    211    info_cache: Dict[Any, Any] 
    212 
    213    @util.deprecated( 
    214        "1.4", 
    215        "The __init__() method on :class:`_reflection.Inspector` " 
    216        "is deprecated and " 
    217        "will be removed in a future release.  Please use the " 
    218        ":func:`.sqlalchemy.inspect` " 
    219        "function on an :class:`_engine.Engine` or " 
    220        ":class:`_engine.Connection` " 
    221        "in order to " 
    222        "acquire an :class:`_reflection.Inspector`.", 
    223    ) 
    224    def __init__(self, bind: Union[Engine, Connection]): 
    225        """Initialize a new :class:`_reflection.Inspector`. 
    226 
    227        :param bind: a :class:`~sqlalchemy.engine.Connection`, 
    228          which is typically an instance of 
    229          :class:`~sqlalchemy.engine.Engine` or 
    230          :class:`~sqlalchemy.engine.Connection`. 
    231 
    232        For a dialect-specific instance of :class:`_reflection.Inspector`, see 
    233        :meth:`_reflection.Inspector.from_engine` 
    234 
    235        """ 
    236        self._init_legacy(bind) 
    237 
    238    @classmethod 
    239    def _construct( 
    240        cls, init: Callable[..., Any], bind: Union[Engine, Connection] 
    241    ) -> Inspector: 
    242        if hasattr(bind.dialect, "inspector"): 
    243            cls = bind.dialect.inspector 
    244 
    245        self = cls.__new__(cls) 
    246        init(self, bind) 
    247        return self 
    248 
    249    def _init_legacy(self, bind: Union[Engine, Connection]) -> None: 
    250        if hasattr(bind, "exec_driver_sql"): 
    251            self._init_connection(bind)  # type: ignore[arg-type] 
    252        else: 
    253            self._init_engine(bind) 
    254 
    255    def _init_engine(self, engine: Engine) -> None: 
    256        self.bind = self.engine = engine 
    257        engine.connect().close() 
    258        self._op_context_requires_connect = True 
    259        self.dialect = self.engine.dialect 
    260        self.info_cache = {} 
    261 
    262    def _init_connection(self, connection: Connection) -> None: 
    263        self.bind = connection 
    264        self.engine = connection.engine 
    265        self._op_context_requires_connect = False 
    266        self.dialect = self.engine.dialect 
    267        self.info_cache = {} 
    268 
    269    def clear_cache(self) -> None: 
    270        """reset the cache for this :class:`.Inspector`. 
    271 
    272        Inspection methods that have data cached will emit SQL queries 
    273        when next called to get new data. 
    274 
    275        .. versionadded:: 2.0 
    276 
    277        """ 
    278        self.info_cache.clear() 
    279 
    280    @classmethod 
    281    @util.deprecated( 
    282        "1.4", 
    283        "The from_engine() method on :class:`_reflection.Inspector` " 
    284        "is deprecated and " 
    285        "will be removed in a future release.  Please use the " 
    286        ":func:`.sqlalchemy.inspect` " 
    287        "function on an :class:`_engine.Engine` or " 
    288        ":class:`_engine.Connection` " 
    289        "in order to " 
    290        "acquire an :class:`_reflection.Inspector`.", 
    291    ) 
    292    def from_engine(cls, bind: Engine) -> Inspector: 
    293        """Construct a new dialect-specific Inspector object from the given 
    294        engine or connection. 
    295 
    296        :param bind: a :class:`~sqlalchemy.engine.Connection` 
    297         or :class:`~sqlalchemy.engine.Engine`. 
    298 
    299        This method differs from direct a direct constructor call of 
    300        :class:`_reflection.Inspector` in that the 
    301        :class:`~sqlalchemy.engine.interfaces.Dialect` is given a chance to 
    302        provide a dialect-specific :class:`_reflection.Inspector` instance, 
    303        which may 
    304        provide additional methods. 
    305 
    306        See the example at :class:`_reflection.Inspector`. 
    307 
    308        """ 
    309        return cls._construct(cls._init_legacy, bind) 
    310 
    311    @inspection._inspects(Engine) 
    312    def _engine_insp(bind: Engine) -> Inspector:  # type: ignore[misc] 
    313        return Inspector._construct(Inspector._init_engine, bind) 
    314 
    315    @inspection._inspects(Connection) 
    316    def _connection_insp(bind: Connection) -> Inspector:  # type: ignore[misc] 
    317        return Inspector._construct(Inspector._init_connection, bind) 
    318 
    319    @contextlib.contextmanager 
    320    def _operation_context(self) -> Generator[Connection, None, None]: 
    321        """Return a context that optimizes for multiple operations on a single 
    322        transaction. 
    323 
    324        This essentially allows connect()/close() to be called if we detected 
    325        that we're against an :class:`_engine.Engine` and not a 
    326        :class:`_engine.Connection`. 
    327 
    328        """ 
    329        conn: Connection 
    330        if self._op_context_requires_connect: 
    331            conn = self.bind.connect()  # type: ignore[union-attr] 
    332        else: 
    333            conn = self.bind  # type: ignore[assignment] 
    334        try: 
    335            yield conn 
    336        finally: 
    337            if self._op_context_requires_connect: 
    338                conn.close() 
    339 
    340    @contextlib.contextmanager 
    341    def _inspection_context(self) -> Generator[Inspector, None, None]: 
    342        """Return an :class:`_reflection.Inspector` 
    343        from this one that will run all 
    344        operations on a single connection. 
    345 
    346        """ 
    347 
    348        with self._operation_context() as conn: 
    349            sub_insp = self._construct(self.__class__._init_connection, conn) 
    350            sub_insp.info_cache = self.info_cache 
    351            yield sub_insp 
    352 
    353    @property 
    354    def default_schema_name(self) -> Optional[str]: 
    355        """Return the default schema name presented by the dialect 
    356        for the current engine's database user. 
    357 
    358        E.g. this is typically ``public`` for PostgreSQL and ``dbo`` 
    359        for SQL Server. 
    360 
    361        """ 
    362        return self.dialect.default_schema_name 
    363 
    364    def get_schema_names(self, **kw: Any) -> List[str]: 
    365        r"""Return all schema names. 
    366 
    367        :param \**kw: Additional keyword argument to pass to the dialect 
    368         specific implementation. See the documentation of the dialect 
    369         in use for more information. 
    370        """ 
    371 
    372        with self._operation_context() as conn: 
    373            return self.dialect.get_schema_names( 
    374                conn, info_cache=self.info_cache, **kw 
    375            ) 
    376 
    377    def get_table_names( 
    378        self, schema: Optional[str] = None, **kw: Any 
    379    ) -> List[str]: 
    380        r"""Return all table names within a particular schema. 
    381 
    382        The names are expected to be real tables only, not views. 
    383        Views are instead returned using the 
    384        :meth:`_reflection.Inspector.get_view_names` and/or 
    385        :meth:`_reflection.Inspector.get_materialized_view_names` 
    386        methods. 
    387 
    388        :param schema: Schema name. If ``schema`` is left at ``None``, the 
    389         database's default schema is 
    390         used, else the named schema is searched.  If the database does not 
    391         support named schemas, behavior is undefined if ``schema`` is not 
    392         passed as ``None``.  For special quoting, use :class:`.quoted_name`. 
    393        :param \**kw: Additional keyword argument to pass to the dialect 
    394         specific implementation. See the documentation of the dialect 
    395         in use for more information. 
    396 
    397        .. seealso:: 
    398 
    399            :meth:`_reflection.Inspector.get_sorted_table_and_fkc_names` 
    400 
    401            :attr:`_schema.MetaData.sorted_tables` 
    402 
    403        """ 
    404 
    405        with self._operation_context() as conn: 
    406            return self.dialect.get_table_names( 
    407                conn, schema, info_cache=self.info_cache, **kw 
    408            ) 
    409 
    410    def has_table( 
    411        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    412    ) -> bool: 
    413        r"""Return True if the backend has a table, view, or temporary 
    414        table of the given name. 
    415 
    416        :param table_name: name of the table to check 
    417        :param schema: schema name to query, if not the default schema. 
    418        :param \**kw: Additional keyword argument to pass to the dialect 
    419         specific implementation. See the documentation of the dialect 
    420         in use for more information. 
    421 
    422        .. versionadded:: 1.4 - the :meth:`.Inspector.has_table` method 
    423           replaces the :meth:`_engine.Engine.has_table` method. 
    424 
    425        .. versionchanged:: 2.0:: :meth:`.Inspector.has_table` now formally 
    426           supports checking for additional table-like objects: 
    427 
    428           * any type of views (plain or materialized) 
    429           * temporary tables of any kind 
    430 
    431           Previously, these two checks were not formally specified and 
    432           different dialects would vary in their behavior.   The dialect 
    433           testing suite now includes tests for all of these object types 
    434           and should be supported by all SQLAlchemy-included dialects. 
    435           Support among third party dialects may be lagging, however. 
    436 
    437        """ 
    438        with self._operation_context() as conn: 
    439            return self.dialect.has_table( 
    440                conn, table_name, schema, info_cache=self.info_cache, **kw 
    441            ) 
    442 
    443    def has_sequence( 
    444        self, sequence_name: str, schema: Optional[str] = None, **kw: Any 
    445    ) -> bool: 
    446        r"""Return True if the backend has a sequence with the given name. 
    447 
    448        :param sequence_name: name of the sequence to check 
    449        :param schema: schema name to query, if not the default schema. 
    450        :param \**kw: Additional keyword argument to pass to the dialect 
    451         specific implementation. See the documentation of the dialect 
    452         in use for more information. 
    453 
    454        .. versionadded:: 1.4 
    455 
    456        """ 
    457        with self._operation_context() as conn: 
    458            return self.dialect.has_sequence( 
    459                conn, sequence_name, schema, info_cache=self.info_cache, **kw 
    460            ) 
    461 
    462    def has_index( 
    463        self, 
    464        table_name: str, 
    465        index_name: str, 
    466        schema: Optional[str] = None, 
    467        **kw: Any, 
    468    ) -> bool: 
    469        r"""Check the existence of a particular index name in the database. 
    470 
    471        :param table_name: the name of the table the index belongs to 
    472        :param index_name: the name of the index to check 
    473        :param schema: schema name to query, if not the default schema. 
    474        :param \**kw: Additional keyword argument to pass to the dialect 
    475         specific implementation. See the documentation of the dialect 
    476         in use for more information. 
    477 
    478        .. versionadded:: 2.0 
    479 
    480        """ 
    481        with self._operation_context() as conn: 
    482            return self.dialect.has_index( 
    483                conn, 
    484                table_name, 
    485                index_name, 
    486                schema, 
    487                info_cache=self.info_cache, 
    488                **kw, 
    489            ) 
    490 
    491    def has_schema(self, schema_name: str, **kw: Any) -> bool: 
    492        r"""Return True if the backend has a schema with the given name. 
    493 
    494        :param schema_name: name of the schema to check 
    495        :param \**kw: Additional keyword argument to pass to the dialect 
    496         specific implementation. See the documentation of the dialect 
    497         in use for more information. 
    498 
    499        .. versionadded:: 2.0 
    500 
    501        """ 
    502        with self._operation_context() as conn: 
    503            return self.dialect.has_schema( 
    504                conn, schema_name, info_cache=self.info_cache, **kw 
    505            ) 
    506 
    507    def get_sorted_table_and_fkc_names( 
    508        self, 
    509        schema: Optional[str] = None, 
    510        **kw: Any, 
    511    ) -> List[Tuple[Optional[str], List[Tuple[str, Optional[str]]]]]: 
    512        r"""Return dependency-sorted table and foreign key constraint names in 
    513        referred to within a particular schema. 
    514 
    515        This will yield 2-tuples of 
    516        ``(tablename, [(tname, fkname), (tname, fkname), ...])`` 
    517        consisting of table names in CREATE order grouped with the foreign key 
    518        constraint names that are not detected as belonging to a cycle. 
    519        The final element 
    520        will be ``(None, [(tname, fkname), (tname, fkname), ..])`` 
    521        which will consist of remaining 
    522        foreign key constraint names that would require a separate CREATE 
    523        step after-the-fact, based on dependencies between tables. 
    524 
    525        :param schema: schema name to query, if not the default schema. 
    526        :param \**kw: Additional keyword argument to pass to the dialect 
    527         specific implementation. See the documentation of the dialect 
    528         in use for more information. 
    529 
    530        .. seealso:: 
    531 
    532            :meth:`_reflection.Inspector.get_table_names` 
    533 
    534            :func:`.sort_tables_and_constraints` - similar method which works 
    535            with an already-given :class:`_schema.MetaData`. 
    536 
    537        """ 
    538 
    539        return [ 
    540            ( 
    541                table_key[1] if table_key else None, 
    542                [(tname, fks) for (_, tname), fks in fk_collection], 
    543            ) 
    544            for ( 
    545                table_key, 
    546                fk_collection, 
    547            ) in self.sort_tables_on_foreign_key_dependency( 
    548                consider_schemas=(schema,) 
    549            ) 
    550        ] 
    551 
    552    def sort_tables_on_foreign_key_dependency( 
    553        self, 
    554        consider_schemas: Collection[Optional[str]] = (None,), 
    555        **kw: Any, 
    556    ) -> List[ 
    557        Tuple[ 
    558            Optional[Tuple[Optional[str], str]], 
    559            List[Tuple[Tuple[Optional[str], str], Optional[str]]], 
    560        ] 
    561    ]: 
    562        r"""Return dependency-sorted table and foreign key constraint names 
    563        referred to within multiple schemas. 
    564 
    565        This method may be compared to 
    566        :meth:`.Inspector.get_sorted_table_and_fkc_names`, which 
    567        works on one schema at a time; here, the method is a generalization 
    568        that will consider multiple schemas at once including that it will 
    569        resolve for cross-schema foreign keys. 
    570 
    571        .. versionadded:: 2.0 
    572 
    573        """ 
    574        SchemaTab = Tuple[Optional[str], str] 
    575 
    576        tuples: Set[Tuple[SchemaTab, SchemaTab]] = set() 
    577        remaining_fkcs: Set[Tuple[SchemaTab, Optional[str]]] = set() 
    578        fknames_for_table: Dict[SchemaTab, Set[Optional[str]]] = {} 
    579        tnames: List[SchemaTab] = [] 
    580 
    581        for schname in consider_schemas: 
    582            schema_fkeys = self.get_multi_foreign_keys(schname, **kw) 
    583            tnames.extend(schema_fkeys) 
    584            for (_, tname), fkeys in schema_fkeys.items(): 
    585                fknames_for_table[(schname, tname)] = { 
    586                    fk["name"] for fk in fkeys 
    587                } 
    588                for fkey in fkeys: 
    589                    if ( 
    590                        tname != fkey["referred_table"] 
    591                        or schname != fkey["referred_schema"] 
    592                    ): 
    593                        tuples.add( 
    594                            ( 
    595                                ( 
    596                                    fkey["referred_schema"], 
    597                                    fkey["referred_table"], 
    598                                ), 
    599                                (schname, tname), 
    600                            ) 
    601                        ) 
    602        try: 
    603            candidate_sort = list(topological.sort(tuples, tnames)) 
    604        except exc.CircularDependencyError as err: 
    605            edge: Tuple[SchemaTab, SchemaTab] 
    606            for edge in err.edges: 
    607                tuples.remove(edge) 
    608                remaining_fkcs.update( 
    609                    (edge[1], fkc) for fkc in fknames_for_table[edge[1]] 
    610                ) 
    611 
    612            candidate_sort = list(topological.sort(tuples, tnames)) 
    613        ret: List[ 
    614            Tuple[Optional[SchemaTab], List[Tuple[SchemaTab, Optional[str]]]] 
    615        ] 
    616        ret = [ 
    617            ( 
    618                (schname, tname), 
    619                [ 
    620                    ((schname, tname), fk) 
    621                    for fk in fknames_for_table[(schname, tname)].difference( 
    622                        name for _, name in remaining_fkcs 
    623                    ) 
    624                ], 
    625            ) 
    626            for (schname, tname) in candidate_sort 
    627        ] 
    628        return ret + [(None, list(remaining_fkcs))] 
    629 
    630    def get_temp_table_names(self, **kw: Any) -> List[str]: 
    631        r"""Return a list of temporary table names for the current bind. 
    632 
    633        This method is unsupported by most dialects; currently 
    634        only Oracle Database, PostgreSQL and SQLite implements it. 
    635 
    636        :param \**kw: Additional keyword argument to pass to the dialect 
    637         specific implementation. See the documentation of the dialect 
    638         in use for more information. 
    639 
    640        """ 
    641 
    642        with self._operation_context() as conn: 
    643            return self.dialect.get_temp_table_names( 
    644                conn, info_cache=self.info_cache, **kw 
    645            ) 
    646 
    647    def get_temp_view_names(self, **kw: Any) -> List[str]: 
    648        r"""Return a list of temporary view names for the current bind. 
    649 
    650        This method is unsupported by most dialects; currently 
    651        only PostgreSQL and SQLite implements it. 
    652 
    653        :param \**kw: Additional keyword argument to pass to the dialect 
    654         specific implementation. See the documentation of the dialect 
    655         in use for more information. 
    656 
    657        """ 
    658        with self._operation_context() as conn: 
    659            return self.dialect.get_temp_view_names( 
    660                conn, info_cache=self.info_cache, **kw 
    661            ) 
    662 
    663    def get_table_options( 
    664        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    665    ) -> Dict[str, Any]: 
    666        r"""Return a dictionary of options specified when the table of the 
    667        given name was created. 
    668 
    669        This currently includes some options that apply to MySQL and Oracle 
    670        Database tables. 
    671 
    672        :param table_name: string name of the table.  For special quoting, 
    673         use :class:`.quoted_name`. 
    674 
    675        :param schema: string schema name; if omitted, uses the default schema 
    676         of the database connection.  For special quoting, 
    677         use :class:`.quoted_name`. 
    678 
    679        :param \**kw: Additional keyword argument to pass to the dialect 
    680         specific implementation. See the documentation of the dialect 
    681         in use for more information. 
    682 
    683        :return: a dict with the table options. The returned keys depend on the 
    684         dialect in use. Each one is prefixed with the dialect name. 
    685 
    686        .. seealso:: :meth:`Inspector.get_multi_table_options` 
    687 
    688        """ 
    689        with self._operation_context() as conn: 
    690            return self.dialect.get_table_options( 
    691                conn, table_name, schema, info_cache=self.info_cache, **kw 
    692            ) 
    693 
    694    def get_multi_table_options( 
    695        self, 
    696        schema: Optional[str] = None, 
    697        filter_names: Optional[Sequence[str]] = None, 
    698        kind: ObjectKind = ObjectKind.TABLE, 
    699        scope: ObjectScope = ObjectScope.DEFAULT, 
    700        **kw: Any, 
    701    ) -> Dict[TableKey, Dict[str, Any]]: 
    702        r"""Return a dictionary of options specified when the tables in the 
    703        given schema were created. 
    704 
    705        The tables can be filtered by passing the names to use to 
    706        ``filter_names``. 
    707 
    708        This currently includes some options that apply to MySQL and Oracle 
    709        tables. 
    710 
    711        :param schema: string schema name; if omitted, uses the default schema 
    712         of the database connection.  For special quoting, 
    713         use :class:`.quoted_name`. 
    714 
    715        :param filter_names: optionally return information only for the 
    716         objects listed here. 
    717 
    718        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    719         to reflect. Defaults to ``ObjectKind.TABLE``. 
    720 
    721        :param scope: a :class:`.ObjectScope` that specifies if options of 
    722         default, temporary or any tables should be reflected. 
    723         Defaults to ``ObjectScope.DEFAULT``. 
    724 
    725        :param \**kw: Additional keyword argument to pass to the dialect 
    726         specific implementation. See the documentation of the dialect 
    727         in use for more information. 
    728 
    729        :return: a dictionary where the keys are two-tuple schema,table-name 
    730         and the values are dictionaries with the table options. 
    731         The returned keys in each dict depend on the 
    732         dialect in use. Each one is prefixed with the dialect name. 
    733         The schema is ``None`` if no schema is provided. 
    734 
    735        .. versionadded:: 2.0 
    736 
    737        .. seealso:: :meth:`Inspector.get_table_options` 
    738        """ 
    739        with self._operation_context() as conn: 
    740            res = self.dialect.get_multi_table_options( 
    741                conn, 
    742                schema=schema, 
    743                filter_names=filter_names, 
    744                kind=kind, 
    745                scope=scope, 
    746                info_cache=self.info_cache, 
    747                **kw, 
    748            ) 
    749            return dict(res) 
    750 
    751    def get_view_names( 
    752        self, schema: Optional[str] = None, **kw: Any 
    753    ) -> List[str]: 
    754        r"""Return all non-materialized view names in `schema`. 
    755 
    756        :param schema: Optional, retrieve names from a non-default schema. 
    757         For special quoting, use :class:`.quoted_name`. 
    758        :param \**kw: Additional keyword argument to pass to the dialect 
    759         specific implementation. See the documentation of the dialect 
    760         in use for more information. 
    761 
    762 
    763        .. versionchanged:: 2.0  For those dialects that previously included 
    764           the names of materialized views in this list (currently PostgreSQL), 
    765           this method no longer returns the names of materialized views. 
    766           the :meth:`.Inspector.get_materialized_view_names` method should 
    767           be used instead. 
    768 
    769        .. seealso:: 
    770 
    771            :meth:`.Inspector.get_materialized_view_names` 
    772 
    773        """ 
    774 
    775        with self._operation_context() as conn: 
    776            return self.dialect.get_view_names( 
    777                conn, schema, info_cache=self.info_cache, **kw 
    778            ) 
    779 
    780    def get_materialized_view_names( 
    781        self, schema: Optional[str] = None, **kw: Any 
    782    ) -> List[str]: 
    783        r"""Return all materialized view names in `schema`. 
    784 
    785        :param schema: Optional, retrieve names from a non-default schema. 
    786         For special quoting, use :class:`.quoted_name`. 
    787        :param \**kw: Additional keyword argument to pass to the dialect 
    788         specific implementation. See the documentation of the dialect 
    789         in use for more information. 
    790 
    791        .. versionadded:: 2.0 
    792 
    793        .. seealso:: 
    794 
    795            :meth:`.Inspector.get_view_names` 
    796 
    797        """ 
    798 
    799        with self._operation_context() as conn: 
    800            return self.dialect.get_materialized_view_names( 
    801                conn, schema, info_cache=self.info_cache, **kw 
    802            ) 
    803 
    804    def get_sequence_names( 
    805        self, schema: Optional[str] = None, **kw: Any 
    806    ) -> List[str]: 
    807        r"""Return all sequence names in `schema`. 
    808 
    809        :param schema: Optional, retrieve names from a non-default schema. 
    810         For special quoting, use :class:`.quoted_name`. 
    811        :param \**kw: Additional keyword argument to pass to the dialect 
    812         specific implementation. See the documentation of the dialect 
    813         in use for more information. 
    814 
    815        """ 
    816 
    817        with self._operation_context() as conn: 
    818            return self.dialect.get_sequence_names( 
    819                conn, schema, info_cache=self.info_cache, **kw 
    820            ) 
    821 
    822    def get_view_definition( 
    823        self, view_name: str, schema: Optional[str] = None, **kw: Any 
    824    ) -> str: 
    825        r"""Return definition for the plain or materialized view called 
    826        ``view_name``. 
    827 
    828        :param view_name: Name of the view. 
    829        :param schema: Optional, retrieve names from a non-default schema. 
    830         For special quoting, use :class:`.quoted_name`. 
    831        :param \**kw: Additional keyword argument to pass to the dialect 
    832         specific implementation. See the documentation of the dialect 
    833         in use for more information. 
    834 
    835        """ 
    836 
    837        with self._operation_context() as conn: 
    838            return self.dialect.get_view_definition( 
    839                conn, view_name, schema, info_cache=self.info_cache, **kw 
    840            ) 
    841 
    842    def get_columns( 
    843        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    844    ) -> List[ReflectedColumn]: 
    845        r"""Return information about columns in ``table_name``. 
    846 
    847        Given a string ``table_name`` and an optional string ``schema``, 
    848        return column information as a list of :class:`.ReflectedColumn`. 
    849 
    850        :param table_name: string name of the table.  For special quoting, 
    851         use :class:`.quoted_name`. 
    852 
    853        :param schema: string schema name; if omitted, uses the default schema 
    854         of the database connection.  For special quoting, 
    855         use :class:`.quoted_name`. 
    856 
    857        :param \**kw: Additional keyword argument to pass to the dialect 
    858         specific implementation. See the documentation of the dialect 
    859         in use for more information. 
    860 
    861        :return: list of dictionaries, each representing the definition of 
    862         a database column. 
    863 
    864        .. seealso:: :meth:`Inspector.get_multi_columns`. 
    865 
    866        """ 
    867 
    868        with self._operation_context() as conn: 
    869            col_defs = self.dialect.get_columns( 
    870                conn, table_name, schema, info_cache=self.info_cache, **kw 
    871            ) 
    872        if col_defs: 
    873            self._instantiate_types([col_defs]) 
    874        return col_defs 
    875 
    876    def _instantiate_types( 
    877        self, data: Iterable[List[ReflectedColumn]] 
    878    ) -> None: 
    879        # make this easy and only return instances for coltype 
    880        for col_defs in data: 
    881            for col_def in col_defs: 
    882                coltype = col_def["type"] 
    883                if not isinstance(coltype, TypeEngine): 
    884                    col_def["type"] = coltype() 
    885 
    886    def get_multi_columns( 
    887        self, 
    888        schema: Optional[str] = None, 
    889        filter_names: Optional[Sequence[str]] = None, 
    890        kind: ObjectKind = ObjectKind.TABLE, 
    891        scope: ObjectScope = ObjectScope.DEFAULT, 
    892        **kw: Any, 
    893    ) -> Dict[TableKey, List[ReflectedColumn]]: 
    894        r"""Return information about columns in all objects in the given 
    895        schema. 
    896 
    897        The objects can be filtered by passing the names to use to 
    898        ``filter_names``. 
    899 
    900        For each table the value is a list of :class:`.ReflectedColumn`. 
    901 
    902        :param schema: string schema name; if omitted, uses the default schema 
    903         of the database connection.  For special quoting, 
    904         use :class:`.quoted_name`. 
    905 
    906        :param filter_names: optionally return information only for the 
    907         objects listed here. 
    908 
    909        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    910         to reflect. Defaults to ``ObjectKind.TABLE``. 
    911 
    912        :param scope: a :class:`.ObjectScope` that specifies if columns of 
    913         default, temporary or any tables should be reflected. 
    914         Defaults to ``ObjectScope.DEFAULT``. 
    915 
    916        :param \**kw: Additional keyword argument to pass to the dialect 
    917         specific implementation. See the documentation of the dialect 
    918         in use for more information. 
    919 
    920        :return: a dictionary where the keys are two-tuple schema,table-name 
    921         and the values are list of dictionaries, each representing the 
    922         definition of a database column. 
    923         The schema is ``None`` if no schema is provided. 
    924 
    925        .. versionadded:: 2.0 
    926 
    927        .. seealso:: :meth:`Inspector.get_columns` 
    928        """ 
    929 
    930        with self._operation_context() as conn: 
    931            table_col_defs = dict( 
    932                self.dialect.get_multi_columns( 
    933                    conn, 
    934                    schema=schema, 
    935                    filter_names=filter_names, 
    936                    kind=kind, 
    937                    scope=scope, 
    938                    info_cache=self.info_cache, 
    939                    **kw, 
    940                ) 
    941            ) 
    942        self._instantiate_types(table_col_defs.values()) 
    943        return table_col_defs 
    944 
    945    def get_pk_constraint( 
    946        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    947    ) -> ReflectedPrimaryKeyConstraint: 
    948        r"""Return information about primary key constraint in ``table_name``. 
    949 
    950        Given a string ``table_name``, and an optional string `schema`, return 
    951        primary key information as a :class:`.ReflectedPrimaryKeyConstraint`. 
    952 
    953        :param table_name: string name of the table.  For special quoting, 
    954         use :class:`.quoted_name`. 
    955 
    956        :param schema: string schema name; if omitted, uses the default schema 
    957         of the database connection.  For special quoting, 
    958         use :class:`.quoted_name`. 
    959 
    960        :param \**kw: Additional keyword argument to pass to the dialect 
    961         specific implementation. See the documentation of the dialect 
    962         in use for more information. 
    963 
    964        :return: a dictionary representing the definition of 
    965         a primary key constraint. 
    966 
    967        .. seealso:: :meth:`Inspector.get_multi_pk_constraint` 
    968        """ 
    969        with self._operation_context() as conn: 
    970            return self.dialect.get_pk_constraint( 
    971                conn, table_name, schema, info_cache=self.info_cache, **kw 
    972            ) 
    973 
    974    def get_multi_pk_constraint( 
    975        self, 
    976        schema: Optional[str] = None, 
    977        filter_names: Optional[Sequence[str]] = None, 
    978        kind: ObjectKind = ObjectKind.TABLE, 
    979        scope: ObjectScope = ObjectScope.DEFAULT, 
    980        **kw: Any, 
    981    ) -> Dict[TableKey, ReflectedPrimaryKeyConstraint]: 
    982        r"""Return information about primary key constraints in 
    983        all tables in the given schema. 
    984 
    985        The tables can be filtered by passing the names to use to 
    986        ``filter_names``. 
    987 
    988        For each table the value is a :class:`.ReflectedPrimaryKeyConstraint`. 
    989 
    990        :param schema: string schema name; if omitted, uses the default schema 
    991         of the database connection.  For special quoting, 
    992         use :class:`.quoted_name`. 
    993 
    994        :param filter_names: optionally return information only for the 
    995         objects listed here. 
    996 
    997        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    998         to reflect. Defaults to ``ObjectKind.TABLE``. 
    999 
    1000        :param scope: a :class:`.ObjectScope` that specifies if primary keys of 
    1001         default, temporary or any tables should be reflected. 
    1002         Defaults to ``ObjectScope.DEFAULT``. 
    1003 
    1004        :param \**kw: Additional keyword argument to pass to the dialect 
    1005         specific implementation. See the documentation of the dialect 
    1006         in use for more information. 
    1007 
    1008        :return: a dictionary where the keys are two-tuple schema,table-name 
    1009         and the values are dictionaries, each representing the 
    1010         definition of a primary key constraint. 
    1011         The schema is ``None`` if no schema is provided. 
    1012 
    1013        .. versionadded:: 2.0 
    1014 
    1015        .. seealso:: :meth:`Inspector.get_pk_constraint` 
    1016        """ 
    1017        with self._operation_context() as conn: 
    1018            return dict( 
    1019                self.dialect.get_multi_pk_constraint( 
    1020                    conn, 
    1021                    schema=schema, 
    1022                    filter_names=filter_names, 
    1023                    kind=kind, 
    1024                    scope=scope, 
    1025                    info_cache=self.info_cache, 
    1026                    **kw, 
    1027                ) 
    1028            ) 
    1029 
    1030    def get_foreign_keys( 
    1031        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    1032    ) -> List[ReflectedForeignKeyConstraint]: 
    1033        r"""Return information about foreign_keys in ``table_name``. 
    1034 
    1035        Given a string ``table_name``, and an optional string `schema`, return 
    1036        foreign key information as a list of 
    1037        :class:`.ReflectedForeignKeyConstraint`. 
    1038 
    1039        :param table_name: string name of the table.  For special quoting, 
    1040         use :class:`.quoted_name`. 
    1041 
    1042        :param schema: string schema name; if omitted, uses the default schema 
    1043         of the database connection.  For special quoting, 
    1044         use :class:`.quoted_name`. 
    1045 
    1046        :param \**kw: Additional keyword argument to pass to the dialect 
    1047         specific implementation. See the documentation of the dialect 
    1048         in use for more information. 
    1049 
    1050        :return: a list of dictionaries, each representing the 
    1051         a foreign key definition. 
    1052 
    1053        .. seealso:: :meth:`Inspector.get_multi_foreign_keys` 
    1054        """ 
    1055 
    1056        with self._operation_context() as conn: 
    1057            return self.dialect.get_foreign_keys( 
    1058                conn, table_name, schema, info_cache=self.info_cache, **kw 
    1059            ) 
    1060 
    1061    def get_multi_foreign_keys( 
    1062        self, 
    1063        schema: Optional[str] = None, 
    1064        filter_names: Optional[Sequence[str]] = None, 
    1065        kind: ObjectKind = ObjectKind.TABLE, 
    1066        scope: ObjectScope = ObjectScope.DEFAULT, 
    1067        **kw: Any, 
    1068    ) -> Dict[TableKey, List[ReflectedForeignKeyConstraint]]: 
    1069        r"""Return information about foreign_keys in all tables 
    1070        in the given schema. 
    1071 
    1072        The tables can be filtered by passing the names to use to 
    1073        ``filter_names``. 
    1074 
    1075        For each table the value is a list of 
    1076        :class:`.ReflectedForeignKeyConstraint`. 
    1077 
    1078        :param schema: string schema name; if omitted, uses the default schema 
    1079         of the database connection.  For special quoting, 
    1080         use :class:`.quoted_name`. 
    1081 
    1082        :param filter_names: optionally return information only for the 
    1083         objects listed here. 
    1084 
    1085        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    1086         to reflect. Defaults to ``ObjectKind.TABLE``. 
    1087 
    1088        :param scope: a :class:`.ObjectScope` that specifies if foreign keys of 
    1089         default, temporary or any tables should be reflected. 
    1090         Defaults to ``ObjectScope.DEFAULT``. 
    1091 
    1092        :param \**kw: Additional keyword argument to pass to the dialect 
    1093         specific implementation. See the documentation of the dialect 
    1094         in use for more information. 
    1095 
    1096        :return: a dictionary where the keys are two-tuple schema,table-name 
    1097         and the values are list of dictionaries, each representing 
    1098         a foreign key definition. 
    1099         The schema is ``None`` if no schema is provided. 
    1100 
    1101        .. versionadded:: 2.0 
    1102 
    1103        .. seealso:: :meth:`Inspector.get_foreign_keys` 
    1104        """ 
    1105 
    1106        with self._operation_context() as conn: 
    1107            return dict( 
    1108                self.dialect.get_multi_foreign_keys( 
    1109                    conn, 
    1110                    schema=schema, 
    1111                    filter_names=filter_names, 
    1112                    kind=kind, 
    1113                    scope=scope, 
    1114                    info_cache=self.info_cache, 
    1115                    **kw, 
    1116                ) 
    1117            ) 
    1118 
    1119    def get_indexes( 
    1120        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    1121    ) -> List[ReflectedIndex]: 
    1122        r"""Return information about indexes in ``table_name``. 
    1123 
    1124        Given a string ``table_name`` and an optional string `schema`, return 
    1125        index information as a list of :class:`.ReflectedIndex`. 
    1126 
    1127        :param table_name: string name of the table.  For special quoting, 
    1128         use :class:`.quoted_name`. 
    1129 
    1130        :param schema: string schema name; if omitted, uses the default schema 
    1131         of the database connection.  For special quoting, 
    1132         use :class:`.quoted_name`. 
    1133 
    1134        :param \**kw: Additional keyword argument to pass to the dialect 
    1135         specific implementation. See the documentation of the dialect 
    1136         in use for more information. 
    1137 
    1138        :return: a list of dictionaries, each representing the 
    1139         definition of an index. 
    1140 
    1141        .. seealso:: :meth:`Inspector.get_multi_indexes` 
    1142        """ 
    1143 
    1144        with self._operation_context() as conn: 
    1145            return self.dialect.get_indexes( 
    1146                conn, table_name, schema, info_cache=self.info_cache, **kw 
    1147            ) 
    1148 
    1149    def get_multi_indexes( 
    1150        self, 
    1151        schema: Optional[str] = None, 
    1152        filter_names: Optional[Sequence[str]] = None, 
    1153        kind: ObjectKind = ObjectKind.TABLE, 
    1154        scope: ObjectScope = ObjectScope.DEFAULT, 
    1155        **kw: Any, 
    1156    ) -> Dict[TableKey, List[ReflectedIndex]]: 
    1157        r"""Return information about indexes in in all objects 
    1158        in the given schema. 
    1159 
    1160        The objects can be filtered by passing the names to use to 
    1161        ``filter_names``. 
    1162 
    1163        For each table the value is a list of :class:`.ReflectedIndex`. 
    1164 
    1165        :param schema: string schema name; if omitted, uses the default schema 
    1166         of the database connection.  For special quoting, 
    1167         use :class:`.quoted_name`. 
    1168 
    1169        :param filter_names: optionally return information only for the 
    1170         objects listed here. 
    1171 
    1172        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    1173         to reflect. Defaults to ``ObjectKind.TABLE``. 
    1174 
    1175        :param scope: a :class:`.ObjectScope` that specifies if indexes of 
    1176         default, temporary or any tables should be reflected. 
    1177         Defaults to ``ObjectScope.DEFAULT``. 
    1178 
    1179        :param \**kw: Additional keyword argument to pass to the dialect 
    1180         specific implementation. See the documentation of the dialect 
    1181         in use for more information. 
    1182 
    1183        :return: a dictionary where the keys are two-tuple schema,table-name 
    1184         and the values are list of dictionaries, each representing the 
    1185         definition of an index. 
    1186         The schema is ``None`` if no schema is provided. 
    1187 
    1188        .. versionadded:: 2.0 
    1189 
    1190        .. seealso:: :meth:`Inspector.get_indexes` 
    1191        """ 
    1192 
    1193        with self._operation_context() as conn: 
    1194            return dict( 
    1195                self.dialect.get_multi_indexes( 
    1196                    conn, 
    1197                    schema=schema, 
    1198                    filter_names=filter_names, 
    1199                    kind=kind, 
    1200                    scope=scope, 
    1201                    info_cache=self.info_cache, 
    1202                    **kw, 
    1203                ) 
    1204            ) 
    1205 
    1206    def get_unique_constraints( 
    1207        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    1208    ) -> List[ReflectedUniqueConstraint]: 
    1209        r"""Return information about unique constraints in ``table_name``. 
    1210 
    1211        Given a string ``table_name`` and an optional string `schema`, return 
    1212        unique constraint information as a list of 
    1213        :class:`.ReflectedUniqueConstraint`. 
    1214 
    1215        :param table_name: string name of the table.  For special quoting, 
    1216         use :class:`.quoted_name`. 
    1217 
    1218        :param schema: string schema name; if omitted, uses the default schema 
    1219         of the database connection.  For special quoting, 
    1220         use :class:`.quoted_name`. 
    1221 
    1222        :param \**kw: Additional keyword argument to pass to the dialect 
    1223         specific implementation. See the documentation of the dialect 
    1224         in use for more information. 
    1225 
    1226        :return: a list of dictionaries, each representing the 
    1227         definition of an unique constraint. 
    1228 
    1229        .. seealso:: :meth:`Inspector.get_multi_unique_constraints` 
    1230        """ 
    1231 
    1232        with self._operation_context() as conn: 
    1233            return self.dialect.get_unique_constraints( 
    1234                conn, table_name, schema, info_cache=self.info_cache, **kw 
    1235            ) 
    1236 
    1237    def get_multi_unique_constraints( 
    1238        self, 
    1239        schema: Optional[str] = None, 
    1240        filter_names: Optional[Sequence[str]] = None, 
    1241        kind: ObjectKind = ObjectKind.TABLE, 
    1242        scope: ObjectScope = ObjectScope.DEFAULT, 
    1243        **kw: Any, 
    1244    ) -> Dict[TableKey, List[ReflectedUniqueConstraint]]: 
    1245        r"""Return information about unique constraints in all tables 
    1246        in the given schema. 
    1247 
    1248        The tables can be filtered by passing the names to use to 
    1249        ``filter_names``. 
    1250 
    1251        For each table the value is a list of 
    1252        :class:`.ReflectedUniqueConstraint`. 
    1253 
    1254        :param schema: string schema name; if omitted, uses the default schema 
    1255         of the database connection.  For special quoting, 
    1256         use :class:`.quoted_name`. 
    1257 
    1258        :param filter_names: optionally return information only for the 
    1259         objects listed here. 
    1260 
    1261        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    1262         to reflect. Defaults to ``ObjectKind.TABLE``. 
    1263 
    1264        :param scope: a :class:`.ObjectScope` that specifies if constraints of 
    1265         default, temporary or any tables should be reflected. 
    1266         Defaults to ``ObjectScope.DEFAULT``. 
    1267 
    1268        :param \**kw: Additional keyword argument to pass to the dialect 
    1269         specific implementation. See the documentation of the dialect 
    1270         in use for more information. 
    1271 
    1272        :return: a dictionary where the keys are two-tuple schema,table-name 
    1273         and the values are list of dictionaries, each representing the 
    1274         definition of an unique constraint. 
    1275         The schema is ``None`` if no schema is provided. 
    1276 
    1277        .. versionadded:: 2.0 
    1278 
    1279        .. seealso:: :meth:`Inspector.get_unique_constraints` 
    1280        """ 
    1281 
    1282        with self._operation_context() as conn: 
    1283            return dict( 
    1284                self.dialect.get_multi_unique_constraints( 
    1285                    conn, 
    1286                    schema=schema, 
    1287                    filter_names=filter_names, 
    1288                    kind=kind, 
    1289                    scope=scope, 
    1290                    info_cache=self.info_cache, 
    1291                    **kw, 
    1292                ) 
    1293            ) 
    1294 
    1295    def get_table_comment( 
    1296        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    1297    ) -> ReflectedTableComment: 
    1298        r"""Return information about the table comment for ``table_name``. 
    1299 
    1300        Given a string ``table_name`` and an optional string ``schema``, 
    1301        return table comment information as a :class:`.ReflectedTableComment`. 
    1302 
    1303        Raises ``NotImplementedError`` for a dialect that does not support 
    1304        comments. 
    1305 
    1306        :param table_name: string name of the table.  For special quoting, 
    1307         use :class:`.quoted_name`. 
    1308 
    1309        :param schema: string schema name; if omitted, uses the default schema 
    1310         of the database connection.  For special quoting, 
    1311         use :class:`.quoted_name`. 
    1312 
    1313        :param \**kw: Additional keyword argument to pass to the dialect 
    1314         specific implementation. See the documentation of the dialect 
    1315         in use for more information. 
    1316 
    1317        :return: a dictionary, with the table comment. 
    1318 
    1319        .. seealso:: :meth:`Inspector.get_multi_table_comment` 
    1320        """ 
    1321 
    1322        with self._operation_context() as conn: 
    1323            return self.dialect.get_table_comment( 
    1324                conn, table_name, schema, info_cache=self.info_cache, **kw 
    1325            ) 
    1326 
    1327    def get_multi_table_comment( 
    1328        self, 
    1329        schema: Optional[str] = None, 
    1330        filter_names: Optional[Sequence[str]] = None, 
    1331        kind: ObjectKind = ObjectKind.TABLE, 
    1332        scope: ObjectScope = ObjectScope.DEFAULT, 
    1333        **kw: Any, 
    1334    ) -> Dict[TableKey, ReflectedTableComment]: 
    1335        r"""Return information about the table comment in all objects 
    1336        in the given schema. 
    1337 
    1338        The objects can be filtered by passing the names to use to 
    1339        ``filter_names``. 
    1340 
    1341        For each table the value is a :class:`.ReflectedTableComment`. 
    1342 
    1343        Raises ``NotImplementedError`` for a dialect that does not support 
    1344        comments. 
    1345 
    1346        :param schema: string schema name; if omitted, uses the default schema 
    1347         of the database connection.  For special quoting, 
    1348         use :class:`.quoted_name`. 
    1349 
    1350        :param filter_names: optionally return information only for the 
    1351         objects listed here. 
    1352 
    1353        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    1354         to reflect. Defaults to ``ObjectKind.TABLE``. 
    1355 
    1356        :param scope: a :class:`.ObjectScope` that specifies if comments of 
    1357         default, temporary or any tables should be reflected. 
    1358         Defaults to ``ObjectScope.DEFAULT``. 
    1359 
    1360        :param \**kw: Additional keyword argument to pass to the dialect 
    1361         specific implementation. See the documentation of the dialect 
    1362         in use for more information. 
    1363 
    1364        :return: a dictionary where the keys are two-tuple schema,table-name 
    1365         and the values are dictionaries, representing the 
    1366         table comments. 
    1367         The schema is ``None`` if no schema is provided. 
    1368 
    1369        .. versionadded:: 2.0 
    1370 
    1371        .. seealso:: :meth:`Inspector.get_table_comment` 
    1372        """ 
    1373 
    1374        with self._operation_context() as conn: 
    1375            return dict( 
    1376                self.dialect.get_multi_table_comment( 
    1377                    conn, 
    1378                    schema=schema, 
    1379                    filter_names=filter_names, 
    1380                    kind=kind, 
    1381                    scope=scope, 
    1382                    info_cache=self.info_cache, 
    1383                    **kw, 
    1384                ) 
    1385            ) 
    1386 
    1387    def get_check_constraints( 
    1388        self, table_name: str, schema: Optional[str] = None, **kw: Any 
    1389    ) -> List[ReflectedCheckConstraint]: 
    1390        r"""Return information about check constraints in ``table_name``. 
    1391 
    1392        Given a string ``table_name`` and an optional string `schema`, return 
    1393        check constraint information as a list of 
    1394        :class:`.ReflectedCheckConstraint`. 
    1395 
    1396        :param table_name: string name of the table.  For special quoting, 
    1397         use :class:`.quoted_name`. 
    1398 
    1399        :param schema: string schema name; if omitted, uses the default schema 
    1400         of the database connection.  For special quoting, 
    1401         use :class:`.quoted_name`. 
    1402 
    1403        :param \**kw: Additional keyword argument to pass to the dialect 
    1404         specific implementation. See the documentation of the dialect 
    1405         in use for more information. 
    1406 
    1407        :return: a list of dictionaries, each representing the 
    1408         definition of a check constraints. 
    1409 
    1410        .. seealso:: :meth:`Inspector.get_multi_check_constraints` 
    1411        """ 
    1412 
    1413        with self._operation_context() as conn: 
    1414            return self.dialect.get_check_constraints( 
    1415                conn, table_name, schema, info_cache=self.info_cache, **kw 
    1416            ) 
    1417 
    1418    def get_multi_check_constraints( 
    1419        self, 
    1420        schema: Optional[str] = None, 
    1421        filter_names: Optional[Sequence[str]] = None, 
    1422        kind: ObjectKind = ObjectKind.TABLE, 
    1423        scope: ObjectScope = ObjectScope.DEFAULT, 
    1424        **kw: Any, 
    1425    ) -> Dict[TableKey, List[ReflectedCheckConstraint]]: 
    1426        r"""Return information about check constraints in all tables 
    1427        in the given schema. 
    1428 
    1429        The tables can be filtered by passing the names to use to 
    1430        ``filter_names``. 
    1431 
    1432        For each table the value is a list of 
    1433        :class:`.ReflectedCheckConstraint`. 
    1434 
    1435        :param schema: string schema name; if omitted, uses the default schema 
    1436         of the database connection.  For special quoting, 
    1437         use :class:`.quoted_name`. 
    1438 
    1439        :param filter_names: optionally return information only for the 
    1440         objects listed here. 
    1441 
    1442        :param kind: a :class:`.ObjectKind` that specifies the type of objects 
    1443         to reflect. Defaults to ``ObjectKind.TABLE``. 
    1444 
    1445        :param scope: a :class:`.ObjectScope` that specifies if constraints of 
    1446         default, temporary or any tables should be reflected. 
    1447         Defaults to ``ObjectScope.DEFAULT``. 
    1448 
    1449        :param \**kw: Additional keyword argument to pass to the dialect 
    1450         specific implementation. See the documentation of the dialect 
    1451         in use for more information. 
    1452 
    1453        :return: a dictionary where the keys are two-tuple schema,table-name 
    1454         and the values are list of dictionaries, each representing the 
    1455         definition of a check constraints. 
    1456         The schema is ``None`` if no schema is provided. 
    1457 
    1458        .. versionadded:: 2.0 
    1459 
    1460        .. seealso:: :meth:`Inspector.get_check_constraints` 
    1461        """ 
    1462 
    1463        with self._operation_context() as conn: 
    1464            return dict( 
    1465                self.dialect.get_multi_check_constraints( 
    1466                    conn, 
    1467                    schema=schema, 
    1468                    filter_names=filter_names, 
    1469                    kind=kind, 
    1470                    scope=scope, 
    1471                    info_cache=self.info_cache, 
    1472                    **kw, 
    1473                ) 
    1474            ) 
    1475 
    1476    def reflect_table( 
    1477        self, 
    1478        table: sa_schema.Table, 
    1479        include_columns: Optional[Collection[str]], 
    1480        exclude_columns: Collection[str] = (), 
    1481        resolve_fks: bool = True, 
    1482        _extend_on: Optional[Set[sa_schema.Table]] = None, 
    1483        _reflect_info: Optional[_ReflectionInfo] = None, 
    1484    ) -> None: 
    1485        """Given a :class:`_schema.Table` object, load its internal 
    1486        constructs based on introspection. 
    1487 
    1488        This is the underlying method used by most dialects to produce 
    1489        table reflection.  Direct usage is like:: 
    1490 
    1491            from sqlalchemy import create_engine, MetaData, Table 
    1492            from sqlalchemy import inspect 
    1493 
    1494            engine = create_engine("...") 
    1495            meta = MetaData() 
    1496            user_table = Table("user", meta) 
    1497            insp = inspect(engine) 
    1498            insp.reflect_table(user_table, None) 
    1499 
    1500        .. versionchanged:: 1.4 Renamed from ``reflecttable`` to 
    1501           ``reflect_table`` 
    1502 
    1503        :param table: a :class:`~sqlalchemy.schema.Table` instance. 
    1504        :param include_columns: a list of string column names to include 
    1505          in the reflection process.  If ``None``, all columns are reflected. 
    1506 
    1507        """ 
    1508 
    1509        if _extend_on is not None: 
    1510            if table in _extend_on: 
    1511                return 
    1512            else: 
    1513                _extend_on.add(table) 
    1514 
    1515        dialect = self.bind.dialect 
    1516 
    1517        with self._operation_context() as conn: 
    1518            schema = conn.schema_for_object(table) 
    1519 
    1520        table_name = table.name 
    1521 
    1522        # get table-level arguments that are specifically 
    1523        # intended for reflection, e.g. oracle_resolve_synonyms. 
    1524        # these are unconditionally passed to related Table 
    1525        # objects 
    1526        reflection_options = { 
    1527            k: table.dialect_kwargs.get(k) 
    1528            for k in dialect.reflection_options 
    1529            if k in table.dialect_kwargs 
    1530        } 
    1531 
    1532        table_key = (schema, table_name) 
    1533        if _reflect_info is None or table_key not in _reflect_info.columns: 
    1534            _reflect_info = self._get_reflection_info( 
    1535                schema, 
    1536                filter_names=[table_name], 
    1537                kind=ObjectKind.ANY, 
    1538                scope=ObjectScope.ANY, 
    1539                _reflect_info=_reflect_info, 
    1540                **table.dialect_kwargs, 
    1541            ) 
    1542        if table_key in _reflect_info.unreflectable: 
    1543            raise _reflect_info.unreflectable[table_key] 
    1544 
    1545        if table_key not in _reflect_info.columns: 
    1546            raise exc.NoSuchTableError(table_name) 
    1547 
    1548        # reflect table options, like mysql_engine 
    1549        if _reflect_info.table_options: 
    1550            tbl_opts = _reflect_info.table_options.get(table_key) 
    1551            if tbl_opts: 
    1552                # add additional kwargs to the Table if the dialect 
    1553                # returned them 
    1554                table._validate_dialect_kwargs(tbl_opts) 
    1555 
    1556        found_table = False 
    1557        cols_by_orig_name: Dict[str, sa_schema.Column[Any]] = {} 
    1558 
    1559        for col_d in _reflect_info.columns[table_key]: 
    1560            found_table = True 
    1561 
    1562            self._reflect_column( 
    1563                table, 
    1564                col_d, 
    1565                include_columns, 
    1566                exclude_columns, 
    1567                cols_by_orig_name, 
    1568            ) 
    1569 
    1570        # NOTE: support tables/views with no columns 
    1571        if not found_table and not self.has_table(table_name, schema): 
    1572            raise exc.NoSuchTableError(table_name) 
    1573 
    1574        self._reflect_pk( 
    1575            _reflect_info, table_key, table, cols_by_orig_name, exclude_columns 
    1576        ) 
    1577 
    1578        self._reflect_fk( 
    1579            _reflect_info, 
    1580            table_key, 
    1581            table, 
    1582            cols_by_orig_name, 
    1583            include_columns, 
    1584            exclude_columns, 
    1585            resolve_fks, 
    1586            _extend_on, 
    1587            reflection_options, 
    1588        ) 
    1589 
    1590        self._reflect_indexes( 
    1591            _reflect_info, 
    1592            table_key, 
    1593            table, 
    1594            cols_by_orig_name, 
    1595            include_columns, 
    1596            exclude_columns, 
    1597            reflection_options, 
    1598        ) 
    1599 
    1600        self._reflect_unique_constraints( 
    1601            _reflect_info, 
    1602            table_key, 
    1603            table, 
    1604            cols_by_orig_name, 
    1605            include_columns, 
    1606            exclude_columns, 
    1607            reflection_options, 
    1608        ) 
    1609 
    1610        self._reflect_check_constraints( 
    1611            _reflect_info, 
    1612            table_key, 
    1613            table, 
    1614            cols_by_orig_name, 
    1615            include_columns, 
    1616            exclude_columns, 
    1617            reflection_options, 
    1618        ) 
    1619 
    1620        self._reflect_table_comment( 
    1621            _reflect_info, 
    1622            table_key, 
    1623            table, 
    1624            reflection_options, 
    1625        ) 
    1626 
    1627    def _reflect_column( 
    1628        self, 
    1629        table: sa_schema.Table, 
    1630        col_d: ReflectedColumn, 
    1631        include_columns: Optional[Collection[str]], 
    1632        exclude_columns: Collection[str], 
    1633        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1634    ) -> None: 
    1635        orig_name = col_d["name"] 
    1636 
    1637        table.metadata.dispatch.column_reflect(self, table, col_d) 
    1638        table.dispatch.column_reflect(self, table, col_d) 
    1639 
    1640        # fetch name again as column_reflect is allowed to 
    1641        # change it 
    1642        name = col_d["name"] 
    1643        if (include_columns and name not in include_columns) or ( 
    1644            exclude_columns and name in exclude_columns 
    1645        ): 
    1646            return 
    1647 
    1648        coltype = col_d["type"] 
    1649 
    1650        col_kw = { 
    1651            k: col_d[k]  # type: ignore[literal-required] 
    1652            for k in [ 
    1653                "nullable", 
    1654                "autoincrement", 
    1655                "quote", 
    1656                "info", 
    1657                "key", 
    1658                "comment", 
    1659            ] 
    1660            if k in col_d 
    1661        } 
    1662 
    1663        if "dialect_options" in col_d: 
    1664            col_kw.update(col_d["dialect_options"]) 
    1665 
    1666        colargs = [] 
    1667        default: Any 
    1668        if col_d.get("default") is not None: 
    1669            default_text = col_d["default"] 
    1670            assert default_text is not None 
    1671            if isinstance(default_text, TextClause): 
    1672                default = sa_schema.DefaultClause( 
    1673                    default_text, _reflected=True 
    1674                ) 
    1675            elif not isinstance(default_text, sa_schema.FetchedValue): 
    1676                default = sa_schema.DefaultClause( 
    1677                    sql.text(default_text), _reflected=True 
    1678                ) 
    1679            else: 
    1680                default = default_text 
    1681            colargs.append(default) 
    1682 
    1683        if "computed" in col_d: 
    1684            computed = sa_schema.Computed(**col_d["computed"]) 
    1685            colargs.append(computed) 
    1686 
    1687        if "identity" in col_d: 
    1688            identity = sa_schema.Identity(**col_d["identity"]) 
    1689            colargs.append(identity) 
    1690 
    1691        cols_by_orig_name[orig_name] = col = sa_schema.Column( 
    1692            name, coltype, *colargs, **col_kw 
    1693        ) 
    1694 
    1695        if col.key in table.primary_key: 
    1696            col.primary_key = True 
    1697        table.append_column(col, replace_existing=True) 
    1698 
    1699    def _reflect_pk( 
    1700        self, 
    1701        _reflect_info: _ReflectionInfo, 
    1702        table_key: TableKey, 
    1703        table: sa_schema.Table, 
    1704        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1705        exclude_columns: Collection[str], 
    1706    ) -> None: 
    1707        pk_cons = _reflect_info.pk_constraint.get(table_key) 
    1708        if pk_cons: 
    1709            pk_cols = [ 
    1710                cols_by_orig_name[pk] 
    1711                for pk in pk_cons["constrained_columns"] 
    1712                if pk in cols_by_orig_name and pk not in exclude_columns 
    1713            ] 
    1714 
    1715            # update pk constraint name, comment and dialect_kwargs 
    1716            table.primary_key.name = pk_cons.get("name") 
    1717            table.primary_key.comment = pk_cons.get("comment", None) 
    1718            dialect_options = pk_cons.get("dialect_options") 
    1719            if dialect_options: 
    1720                table.primary_key.dialect_kwargs.update(dialect_options) 
    1721 
    1722            # tell the PKConstraint to re-initialize 
    1723            # its column collection 
    1724            table.primary_key._reload(pk_cols) 
    1725 
    1726    def _reflect_fk( 
    1727        self, 
    1728        _reflect_info: _ReflectionInfo, 
    1729        table_key: TableKey, 
    1730        table: sa_schema.Table, 
    1731        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1732        include_columns: Optional[Collection[str]], 
    1733        exclude_columns: Collection[str], 
    1734        resolve_fks: bool, 
    1735        _extend_on: Optional[Set[sa_schema.Table]], 
    1736        reflection_options: Dict[str, Any], 
    1737    ) -> None: 
    1738        fkeys = _reflect_info.foreign_keys.get(table_key, []) 
    1739        for fkey_d in fkeys: 
    1740            conname = fkey_d["name"] 
    1741            # look for columns by orig name in cols_by_orig_name, 
    1742            # but support columns that are in-Python only as fallback 
    1743            constrained_columns = [ 
    1744                cols_by_orig_name[c].key if c in cols_by_orig_name else c 
    1745                for c in fkey_d["constrained_columns"] 
    1746            ] 
    1747 
    1748            if ( 
    1749                exclude_columns 
    1750                and set(constrained_columns).intersection(exclude_columns) 
    1751                or ( 
    1752                    include_columns 
    1753                    and set(constrained_columns).difference(include_columns) 
    1754                ) 
    1755            ): 
    1756                continue 
    1757 
    1758            referred_schema = fkey_d["referred_schema"] 
    1759            referred_table = fkey_d["referred_table"] 
    1760            referred_columns = fkey_d["referred_columns"] 
    1761            refspec = [] 
    1762            if referred_schema is not None: 
    1763                if resolve_fks: 
    1764                    sa_schema.Table( 
    1765                        referred_table, 
    1766                        table.metadata, 
    1767                        schema=referred_schema, 
    1768                        autoload_with=self.bind, 
    1769                        _extend_on=_extend_on, 
    1770                        _reflect_info=_reflect_info, 
    1771                        **reflection_options, 
    1772                    ) 
    1773                for column in referred_columns: 
    1774                    refspec.append( 
    1775                        ".".join([referred_schema, referred_table, column]) 
    1776                    ) 
    1777            else: 
    1778                if resolve_fks: 
    1779                    sa_schema.Table( 
    1780                        referred_table, 
    1781                        table.metadata, 
    1782                        autoload_with=self.bind, 
    1783                        schema=sa_schema.BLANK_SCHEMA, 
    1784                        _extend_on=_extend_on, 
    1785                        _reflect_info=_reflect_info, 
    1786                        **reflection_options, 
    1787                    ) 
    1788                for column in referred_columns: 
    1789                    refspec.append(".".join([referred_table, column])) 
    1790            if "options" in fkey_d: 
    1791                options = fkey_d["options"] 
    1792            else: 
    1793                options = {} 
    1794 
    1795            try: 
    1796                table.append_constraint( 
    1797                    sa_schema.ForeignKeyConstraint( 
    1798                        constrained_columns, 
    1799                        refspec, 
    1800                        conname, 
    1801                        link_to_name=True, 
    1802                        comment=fkey_d.get("comment"), 
    1803                        **options, 
    1804                    ) 
    1805                ) 
    1806            except exc.ConstraintColumnNotFoundError: 
    1807                util.warn( 
    1808                    f"On reflected table {table.name}, skipping reflection of " 
    1809                    "foreign key constraint " 
    1810                    f"{conname}; one or more subject columns within " 
    1811                    f"name(s) {', '.join(constrained_columns)} are not " 
    1812                    "present in the table" 
    1813                ) 
    1814 
    1815    _index_sort_exprs = { 
    1816        "asc": operators.asc_op, 
    1817        "desc": operators.desc_op, 
    1818        "nulls_first": operators.nulls_first_op, 
    1819        "nulls_last": operators.nulls_last_op, 
    1820    } 
    1821 
    1822    def _reflect_indexes( 
    1823        self, 
    1824        _reflect_info: _ReflectionInfo, 
    1825        table_key: TableKey, 
    1826        table: sa_schema.Table, 
    1827        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1828        include_columns: Optional[Collection[str]], 
    1829        exclude_columns: Collection[str], 
    1830        reflection_options: Dict[str, Any], 
    1831    ) -> None: 
    1832        # Indexes 
    1833        indexes = _reflect_info.indexes.get(table_key, []) 
    1834        for index_d in indexes: 
    1835            name = index_d["name"] 
    1836            columns = index_d["column_names"] 
    1837            expressions = index_d.get("expressions") 
    1838            column_sorting = index_d.get("column_sorting", {}) 
    1839            unique = index_d["unique"] 
    1840            flavor = index_d.get("type", "index") 
    1841            dialect_options = index_d.get("dialect_options", {}) 
    1842 
    1843            duplicates = index_d.get("duplicates_constraint") 
    1844            if include_columns and not set(columns).issubset(include_columns): 
    1845                continue 
    1846            if duplicates: 
    1847                continue 
    1848            # look for columns by orig name in cols_by_orig_name, 
    1849            # but support columns that are in-Python only as fallback 
    1850            idx_element: Any 
    1851            idx_elements = [] 
    1852            for index, c in enumerate(columns): 
    1853                if c is None: 
    1854                    if not expressions: 
    1855                        util.warn( 
    1856                            f"Skipping {flavor} {name!r} because key " 
    1857                            f"{index + 1} reflected as None but no " 
    1858                            "'expressions' were returned" 
    1859                        ) 
    1860                        break 
    1861                    idx_element = sql.text(expressions[index]) 
    1862                else: 
    1863                    try: 
    1864                        if c in cols_by_orig_name: 
    1865                            idx_element = cols_by_orig_name[c] 
    1866                        else: 
    1867                            idx_element = table.c[c] 
    1868                    except KeyError: 
    1869                        util.warn( 
    1870                            f"{flavor} key {c!r} was not located in " 
    1871                            f"columns for table {table.name!r}" 
    1872                        ) 
    1873                        continue 
    1874                    for option in column_sorting.get(c, ()): 
    1875                        if option in self._index_sort_exprs: 
    1876                            op = self._index_sort_exprs[option] 
    1877                            idx_element = op(idx_element) 
    1878                idx_elements.append(idx_element) 
    1879            else: 
    1880                sa_schema.Index( 
    1881                    name, 
    1882                    *idx_elements, 
    1883                    _table=table, 
    1884                    unique=unique, 
    1885                    **dialect_options, 
    1886                ) 
    1887 
    1888    def _reflect_unique_constraints( 
    1889        self, 
    1890        _reflect_info: _ReflectionInfo, 
    1891        table_key: TableKey, 
    1892        table: sa_schema.Table, 
    1893        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1894        include_columns: Optional[Collection[str]], 
    1895        exclude_columns: Collection[str], 
    1896        reflection_options: Dict[str, Any], 
    1897    ) -> None: 
    1898        constraints = _reflect_info.unique_constraints.get(table_key, []) 
    1899        # Unique Constraints 
    1900        for const_d in constraints: 
    1901            conname = const_d["name"] 
    1902            columns = const_d["column_names"] 
    1903            comment = const_d.get("comment") 
    1904            duplicates = const_d.get("duplicates_index") 
    1905            dialect_options = const_d.get("dialect_options", {}) 
    1906            if include_columns and not set(columns).issubset(include_columns): 
    1907                continue 
    1908            if duplicates: 
    1909                continue 
    1910            # look for columns by orig name in cols_by_orig_name, 
    1911            # but support columns that are in-Python only as fallback 
    1912            constrained_cols = [] 
    1913            for c in columns: 
    1914                try: 
    1915                    constrained_col = ( 
    1916                        cols_by_orig_name[c] 
    1917                        if c in cols_by_orig_name 
    1918                        else table.c[c] 
    1919                    ) 
    1920                except KeyError: 
    1921                    util.warn( 
    1922                        "unique constraint key '%s' was not located in " 
    1923                        "columns for table '%s'" % (c, table.name) 
    1924                    ) 
    1925                else: 
    1926                    constrained_cols.append(constrained_col) 
    1927            table.append_constraint( 
    1928                sa_schema.UniqueConstraint( 
    1929                    *constrained_cols, 
    1930                    name=conname, 
    1931                    comment=comment, 
    1932                    **dialect_options, 
    1933                ) 
    1934            ) 
    1935 
    1936    def _reflect_check_constraints( 
    1937        self, 
    1938        _reflect_info: _ReflectionInfo, 
    1939        table_key: TableKey, 
    1940        table: sa_schema.Table, 
    1941        cols_by_orig_name: Dict[str, sa_schema.Column[Any]], 
    1942        include_columns: Optional[Collection[str]], 
    1943        exclude_columns: Collection[str], 
    1944        reflection_options: Dict[str, Any], 
    1945    ) -> None: 
    1946        constraints = _reflect_info.check_constraints.get(table_key, []) 
    1947        for const_d in constraints: 
    1948            table.append_constraint(sa_schema.CheckConstraint(**const_d)) 
    1949 
    1950    def _reflect_table_comment( 
    1951        self, 
    1952        _reflect_info: _ReflectionInfo, 
    1953        table_key: TableKey, 
    1954        table: sa_schema.Table, 
    1955        reflection_options: Dict[str, Any], 
    1956    ) -> None: 
    1957        comment_dict = _reflect_info.table_comment.get(table_key) 
    1958        if comment_dict: 
    1959            table.comment = comment_dict["text"] 
    1960 
    1961    def _get_reflection_info( 
    1962        self, 
    1963        schema: Optional[str] = None, 
    1964        filter_names: Optional[Collection[str]] = None, 
    1965        available: Optional[Collection[str]] = None, 
    1966        _reflect_info: Optional[_ReflectionInfo] = None, 
    1967        **kw: Any, 
    1968    ) -> _ReflectionInfo: 
    1969        kw["schema"] = schema 
    1970 
    1971        if filter_names and available and len(filter_names) > 100: 
    1972            fraction = len(filter_names) / len(available) 
    1973        else: 
    1974            fraction = None 
    1975 
    1976        unreflectable: Dict[TableKey, exc.UnreflectableTableError] 
    1977        kw["unreflectable"] = unreflectable = {} 
    1978 
    1979        has_result: bool = True 
    1980 
    1981        def run( 
    1982            meth: Any, 
    1983            *, 
    1984            optional: bool = False, 
    1985            check_filter_names_from_meth: bool = False, 
    1986        ) -> Any: 
    1987            nonlocal has_result 
    1988            # simple heuristic to improve reflection performance if a 
    1989            # dialect implements multi_reflection: 
    1990            # if more than 50% of the tables in the db are in filter_names 
    1991            # load all the tables, since it's most likely faster to avoid 
    1992            # a filter on that many tables. 
    1993            if ( 
    1994                fraction is None 
    1995                or fraction <= 0.5 
    1996                or not self.dialect._overrides_default(meth.__name__) 
    1997            ): 
    1998                _fn = filter_names 
    1999            else: 
    2000                _fn = None 
    2001            try: 
    2002                if has_result: 
    2003                    res = meth(filter_names=_fn, **kw) 
    2004                    if check_filter_names_from_meth and not res: 
    2005                        # method returned no result data. 
    2006                        # skip any future call methods 
    2007                        has_result = False 
    2008                else: 
    2009                    res = {} 
    2010            except NotImplementedError: 
    2011                if not optional: 
    2012                    raise 
    2013                res = {} 
    2014            return res 
    2015 
    2016        info = _ReflectionInfo( 
    2017            columns=run( 
    2018                self.get_multi_columns, check_filter_names_from_meth=True 
    2019            ), 
    2020            pk_constraint=run(self.get_multi_pk_constraint), 
    2021            foreign_keys=run(self.get_multi_foreign_keys), 
    2022            indexes=run(self.get_multi_indexes), 
    2023            unique_constraints=run( 
    2024                self.get_multi_unique_constraints, optional=True 
    2025            ), 
    2026            table_comment=run(self.get_multi_table_comment, optional=True), 
    2027            check_constraints=run( 
    2028                self.get_multi_check_constraints, optional=True 
    2029            ), 
    2030            table_options=run(self.get_multi_table_options, optional=True), 
    2031            unreflectable=unreflectable, 
    2032        ) 
    2033        if _reflect_info: 
    2034            _reflect_info.update(info) 
    2035            return _reflect_info 
    2036        else: 
    2037            return info 
    2038 
    2039 
    2040@final 
    2041class ReflectionDefaults: 
    2042    """provides blank default values for reflection methods.""" 
    2043 
    2044    @classmethod 
    2045    def columns(cls) -> List[ReflectedColumn]: 
    2046        return [] 
    2047 
    2048    @classmethod 
    2049    def pk_constraint(cls) -> ReflectedPrimaryKeyConstraint: 
    2050        return { 
    2051            "name": None, 
    2052            "constrained_columns": [], 
    2053        } 
    2054 
    2055    @classmethod 
    2056    def foreign_keys(cls) -> List[ReflectedForeignKeyConstraint]: 
    2057        return [] 
    2058 
    2059    @classmethod 
    2060    def indexes(cls) -> List[ReflectedIndex]: 
    2061        return [] 
    2062 
    2063    @classmethod 
    2064    def unique_constraints(cls) -> List[ReflectedUniqueConstraint]: 
    2065        return [] 
    2066 
    2067    @classmethod 
    2068    def check_constraints(cls) -> List[ReflectedCheckConstraint]: 
    2069        return [] 
    2070 
    2071    @classmethod 
    2072    def table_options(cls) -> Dict[str, Any]: 
    2073        return {} 
    2074 
    2075    @classmethod 
    2076    def table_comment(cls) -> ReflectedTableComment: 
    2077        return {"text": None} 
    2078 
    2079 
    2080@dataclass 
    2081class _ReflectionInfo: 
    2082    columns: Dict[TableKey, List[ReflectedColumn]] 
    2083    pk_constraint: Dict[TableKey, Optional[ReflectedPrimaryKeyConstraint]] 
    2084    foreign_keys: Dict[TableKey, List[ReflectedForeignKeyConstraint]] 
    2085    indexes: Dict[TableKey, List[ReflectedIndex]] 
    2086    # optionals 
    2087    unique_constraints: Dict[TableKey, List[ReflectedUniqueConstraint]] 
    2088    table_comment: Dict[TableKey, Optional[ReflectedTableComment]] 
    2089    check_constraints: Dict[TableKey, List[ReflectedCheckConstraint]] 
    2090    table_options: Dict[TableKey, Dict[str, Any]] 
    2091    unreflectable: Dict[TableKey, exc.UnreflectableTableError] 
    2092 
    2093    def update(self, other: _ReflectionInfo) -> None: 
    2094        for k, v in self.__dict__.items(): 
    2095            ov = getattr(other, k) 
    2096            if ov is not None: 
    2097                if v is None: 
    2098                    setattr(self, k, ov) 
    2099                else: 
    2100                    v.update(ov)