1# sql/events.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 
    8from __future__ import annotations 
    9 
    10from typing import Any 
    11from typing import TYPE_CHECKING 
    12 
    13from .base import SchemaEventTarget 
    14from .. import event 
    15 
    16if TYPE_CHECKING: 
    17    from .schema import Column 
    18    from .schema import Constraint 
    19    from .schema import SchemaItem 
    20    from .schema import Table 
    21    from ..engine.base import Connection 
    22    from ..engine.interfaces import ReflectedColumn 
    23    from ..engine.reflection import Inspector 
    24 
    25 
    26class DDLEvents(event.Events[SchemaEventTarget]): 
    27    """ 
    28    Define event listeners for schema objects, 
    29    that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget` 
    30    subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`, 
    31    :class:`_schema.Column`, etc. 
    32 
    33    **Create / Drop Events** 
    34 
    35    Events emitted when CREATE and DROP commands are emitted to the database. 
    36    The event hooks in this category include :meth:`.DDLEvents.before_create`, 
    37    :meth:`.DDLEvents.after_create`, :meth:`.DDLEvents.before_drop`, and 
    38    :meth:`.DDLEvents.after_drop`. 
    39 
    40    These events are emitted when using schema-level methods such as 
    41    :meth:`.MetaData.create_all` and :meth:`.MetaData.drop_all`. Per-object 
    42    create/drop methods such as :meth:`.Table.create`, :meth:`.Table.drop`, 
    43    :meth:`.Index.create` are also included, as well as dialect-specific 
    44    methods such as :meth:`_postgresql.ENUM.create`. 
    45 
    46    .. versionadded:: 2.0 :class:`.DDLEvents` event hooks now take place 
    47       for non-table objects including constraints, indexes, and 
    48       dialect-specific schema types. 
    49 
    50    Event hooks may be attached directly to a :class:`_schema.Table` object or 
    51    to a :class:`_schema.MetaData` collection, as well as to any 
    52    :class:`.SchemaItem` class or object that can be individually created and 
    53    dropped using a distinct SQL command. Such classes include :class:`.Index`, 
    54    :class:`.Sequence`, and dialect-specific classes such as 
    55    :class:`_postgresql.ENUM`. 
    56 
    57    Example using the :meth:`.DDLEvents.after_create` event, where a custom 
    58    event hook will emit an ``ALTER TABLE`` command on the current connection, 
    59    after ``CREATE TABLE`` is emitted:: 
    60 
    61        from sqlalchemy import create_engine 
    62        from sqlalchemy import event 
    63        from sqlalchemy import Table, Column, Metadata, Integer 
    64 
    65        m = MetaData() 
    66        some_table = Table("some_table", m, Column("data", Integer)) 
    67 
    68 
    69        @event.listens_for(some_table, "after_create") 
    70        def after_create(target, connection, **kw): 
    71            connection.execute( 
    72                text("ALTER TABLE %s SET name=foo_%s" % (target.name, target.name)) 
    73            ) 
    74 
    75 
    76        some_engine = create_engine("postgresql://scott:tiger@host/test") 
    77 
    78        # will emit "CREATE TABLE some_table" as well as the above 
    79        # "ALTER TABLE" statement afterwards 
    80        m.create_all(some_engine) 
    81 
    82    Constraint objects such as :class:`.ForeignKeyConstraint`, 
    83    :class:`.UniqueConstraint`, :class:`.CheckConstraint` may also be 
    84    subscribed to these events, however they will **not** normally produce 
    85    events as these objects are usually rendered inline within an 
    86    enclosing ``CREATE TABLE`` statement and implicitly dropped from a 
    87    ``DROP TABLE`` statement. 
    88 
    89    For the :class:`.Index` construct, the event hook will be emitted 
    90    for ``CREATE INDEX``, however SQLAlchemy does not normally emit 
    91    ``DROP INDEX`` when dropping tables as this is again implicit within the 
    92    ``DROP TABLE`` statement. 
    93 
    94    .. versionadded:: 2.0 Support for :class:`.SchemaItem` objects 
    95       for create/drop events was expanded from its previous support for 
    96       :class:`.MetaData` and :class:`.Table` to also include 
    97       :class:`.Constraint` and all subclasses, :class:`.Index`, 
    98       :class:`.Sequence` and some type-related constructs such as 
    99       :class:`_postgresql.ENUM`. 
    100 
    101    .. note:: These event hooks are only emitted within the scope of 
    102       SQLAlchemy's create/drop methods; they are not necessarily supported 
    103       by tools such as `alembic <https://alembic.sqlalchemy.org>`_. 
    104 
    105 
    106    **Attachment Events** 
    107 
    108    Attachment events are provided to customize 
    109    behavior whenever a child schema element is associated 
    110    with a parent, such as when a :class:`_schema.Column` is associated 
    111    with its :class:`_schema.Table`, when a 
    112    :class:`_schema.ForeignKeyConstraint` 
    113    is associated with a :class:`_schema.Table`, etc.  These events include 
    114    :meth:`.DDLEvents.before_parent_attach` and 
    115    :meth:`.DDLEvents.after_parent_attach`. 
    116 
    117    **Reflection Events** 
    118 
    119    The :meth:`.DDLEvents.column_reflect` event is used to intercept 
    120    and modify the in-Python definition of database columns when 
    121    :term:`reflection` of database tables proceeds. 
    122 
    123    **Use with Generic DDL** 
    124 
    125    DDL events integrate closely with the 
    126    :class:`.DDL` class and the :class:`.ExecutableDDLElement` hierarchy 
    127    of DDL clause constructs, which are themselves appropriate 
    128    as listener callables:: 
    129 
    130        from sqlalchemy import DDL 
    131 
    132        event.listen( 
    133            some_table, 
    134            "after_create", 
    135            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"), 
    136        ) 
    137 
    138    **Event Propagation to MetaData Copies** 
    139 
    140    For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument 
    141    will ensure that a given event handler is propagated to copies of the 
    142    object, which are made when using the :meth:`_schema.Table.to_metadata` 
    143    method:: 
    144 
    145        from sqlalchemy import DDL 
    146 
    147        metadata = MetaData() 
    148        some_table = Table("some_table", metadata, Column("data", Integer)) 
    149 
    150        event.listen( 
    151            some_table, 
    152            "after_create", 
    153            DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"), 
    154            propagate=True, 
    155        ) 
    156 
    157        new_metadata = MetaData() 
    158        new_table = some_table.to_metadata(new_metadata) 
    159 
    160    The above :class:`.DDL` object will be associated with the 
    161    :meth:`.DDLEvents.after_create` event for both the ``some_table`` and 
    162    the ``new_table`` :class:`.Table` objects. 
    163 
    164    .. seealso:: 
    165 
    166        :ref:`event_toplevel` 
    167 
    168        :class:`.ExecutableDDLElement` 
    169 
    170        :class:`.DDL` 
    171 
    172        :ref:`schema_ddl_sequences` 
    173 
    174    """  # noqa: E501 
    175 
    176    _target_class_doc = "SomeSchemaClassOrObject" 
    177    _dispatch_target = SchemaEventTarget 
    178 
    179    def before_create( 
    180        self, target: SchemaEventTarget, connection: Connection, **kw: Any 
    181    ) -> None: 
    182        r"""Called before CREATE statements are emitted. 
    183 
    184        :param target: the :class:`.SchemaObject`, such as a 
    185         :class:`_schema.MetaData` or :class:`_schema.Table` 
    186         but also including all create/drop objects such as 
    187         :class:`.Index`, :class:`.Sequence`, etc., 
    188         object which is the target of the event. 
    189 
    190         .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects 
    191            was added. 
    192 
    193        :param connection: the :class:`_engine.Connection` where the 
    194         CREATE statement or statements will be emitted. 
    195        :param \**kw: additional keyword arguments relevant 
    196         to the event.  The contents of this dictionary 
    197         may vary across releases, and include the 
    198         list of tables being generated for a metadata-level 
    199         event, the checkfirst flag, and other 
    200         elements used by internal events. 
    201 
    202        :func:`.event.listen` accepts the ``propagate=True`` 
    203        modifier for this event; when True, the listener function will 
    204        be established for any copies made of the target object, 
    205        i.e. those copies that are generated when 
    206        :meth:`_schema.Table.to_metadata` is used. 
    207 
    208        :func:`.event.listen` accepts the ``insert=True`` 
    209        modifier for this event; when True, the listener function will 
    210        be prepended to the internal list of events upon discovery, and execute 
    211        before registered listener functions that do not pass this argument. 
    212 
    213        """ 
    214 
    215    def after_create( 
    216        self, target: SchemaEventTarget, connection: Connection, **kw: Any 
    217    ) -> None: 
    218        r"""Called after CREATE statements are emitted. 
    219 
    220        :param target: the :class:`.SchemaObject`, such as a 
    221         :class:`_schema.MetaData` or :class:`_schema.Table` 
    222         but also including all create/drop objects such as 
    223         :class:`.Index`, :class:`.Sequence`, etc., 
    224         object which is the target of the event. 
    225 
    226         .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects 
    227            was added. 
    228 
    229        :param connection: the :class:`_engine.Connection` where the 
    230         CREATE statement or statements have been emitted. 
    231        :param \**kw: additional keyword arguments relevant 
    232         to the event.  The contents of this dictionary 
    233         may vary across releases, and include the 
    234         list of tables being generated for a metadata-level 
    235         event, the checkfirst flag, and other 
    236         elements used by internal events. 
    237 
    238        :func:`.event.listen` also accepts the ``propagate=True`` 
    239        modifier for this event; when True, the listener function will 
    240        be established for any copies made of the target object, 
    241        i.e. those copies that are generated when 
    242        :meth:`_schema.Table.to_metadata` is used. 
    243 
    244        """ 
    245 
    246    def before_drop( 
    247        self, target: SchemaEventTarget, connection: Connection, **kw: Any 
    248    ) -> None: 
    249        r"""Called before DROP statements are emitted. 
    250 
    251        :param target: the :class:`.SchemaObject`, such as a 
    252         :class:`_schema.MetaData` or :class:`_schema.Table` 
    253         but also including all create/drop objects such as 
    254         :class:`.Index`, :class:`.Sequence`, etc., 
    255         object which is the target of the event. 
    256 
    257         .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects 
    258            was added. 
    259 
    260        :param connection: the :class:`_engine.Connection` where the 
    261         DROP statement or statements will be emitted. 
    262        :param \**kw: additional keyword arguments relevant 
    263         to the event.  The contents of this dictionary 
    264         may vary across releases, and include the 
    265         list of tables being generated for a metadata-level 
    266         event, the checkfirst flag, and other 
    267         elements used by internal events. 
    268 
    269        :func:`.event.listen` also accepts the ``propagate=True`` 
    270        modifier for this event; when True, the listener function will 
    271        be established for any copies made of the target object, 
    272        i.e. those copies that are generated when 
    273        :meth:`_schema.Table.to_metadata` is used. 
    274 
    275        """ 
    276 
    277    def after_drop( 
    278        self, target: SchemaEventTarget, connection: Connection, **kw: Any 
    279    ) -> None: 
    280        r"""Called after DROP statements are emitted. 
    281 
    282        :param target: the :class:`.SchemaObject`, such as a 
    283         :class:`_schema.MetaData` or :class:`_schema.Table` 
    284         but also including all create/drop objects such as 
    285         :class:`.Index`, :class:`.Sequence`, etc., 
    286         object which is the target of the event. 
    287 
    288         .. versionadded:: 2.0 Support for all :class:`.SchemaItem` objects 
    289            was added. 
    290 
    291        :param connection: the :class:`_engine.Connection` where the 
    292         DROP statement or statements have been emitted. 
    293        :param \**kw: additional keyword arguments relevant 
    294         to the event.  The contents of this dictionary 
    295         may vary across releases, and include the 
    296         list of tables being generated for a metadata-level 
    297         event, the checkfirst flag, and other 
    298         elements used by internal events. 
    299 
    300        :func:`.event.listen` also accepts the ``propagate=True`` 
    301        modifier for this event; when True, the listener function will 
    302        be established for any copies made of the target object, 
    303        i.e. those copies that are generated when 
    304        :meth:`_schema.Table.to_metadata` is used. 
    305 
    306        """ 
    307 
    308    def before_parent_attach( 
    309        self, target: SchemaEventTarget, parent: SchemaItem 
    310    ) -> None: 
    311        """Called before a :class:`.SchemaItem` is associated with 
    312        a parent :class:`.SchemaItem`. 
    313 
    314        :param target: the target object 
    315        :param parent: the parent to which the target is being attached. 
    316 
    317        :func:`.event.listen` also accepts the ``propagate=True`` 
    318        modifier for this event; when True, the listener function will 
    319        be established for any copies made of the target object, 
    320        i.e. those copies that are generated when 
    321        :meth:`_schema.Table.to_metadata` is used. 
    322 
    323        """ 
    324 
    325    def after_parent_attach( 
    326        self, target: SchemaEventTarget, parent: SchemaItem 
    327    ) -> None: 
    328        """Called after a :class:`.SchemaItem` is associated with 
    329        a parent :class:`.SchemaItem`. 
    330 
    331        :param target: the target object 
    332        :param parent: the parent to which the target is being attached. 
    333 
    334        :func:`.event.listen` also accepts the ``propagate=True`` 
    335        modifier for this event; when True, the listener function will 
    336        be established for any copies made of the target object, 
    337        i.e. those copies that are generated when 
    338        :meth:`_schema.Table.to_metadata` is used. 
    339 
    340        """ 
    341 
    342    def _sa_event_column_added_to_pk_constraint( 
    343        self, const: Constraint, col: Column[Any] 
    344    ) -> None: 
    345        """internal event hook used for primary key naming convention 
    346        updates. 
    347 
    348        """ 
    349 
    350    def column_reflect( 
    351        self, inspector: Inspector, table: Table, column_info: ReflectedColumn 
    352    ) -> None: 
    353        """Called for each unit of 'column info' retrieved when 
    354        a :class:`_schema.Table` is being reflected. 
    355 
    356        This event is most easily used by applying it to a specific 
    357        :class:`_schema.MetaData` instance, where it will take effect for 
    358        all :class:`_schema.Table` objects within that 
    359        :class:`_schema.MetaData` that undergo reflection:: 
    360 
    361            metadata = MetaData() 
    362 
    363 
    364            @event.listens_for(metadata, "column_reflect") 
    365            def receive_column_reflect(inspector, table, column_info): 
    366                # receives for all Table objects that are reflected 
    367                # under this MetaData 
    368                ... 
    369 
    370 
    371            # will use the above event hook 
    372            my_table = Table("my_table", metadata, autoload_with=some_engine) 
    373 
    374        .. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect` 
    375           hook may now be applied to a :class:`_schema.MetaData` object as 
    376           well as the :class:`_schema.MetaData` class itself where it will 
    377           take place for all :class:`_schema.Table` objects associated with 
    378           the targeted :class:`_schema.MetaData`. 
    379 
    380        It may also be applied to the :class:`_schema.Table` class across 
    381        the board:: 
    382 
    383            from sqlalchemy import Table 
    384 
    385 
    386            @event.listens_for(Table, "column_reflect") 
    387            def receive_column_reflect(inspector, table, column_info): 
    388                # receives for all Table objects that are reflected 
    389                ... 
    390 
    391        It can also be applied to a specific :class:`_schema.Table` at the 
    392        point that one is being reflected using the 
    393        :paramref:`_schema.Table.listeners` parameter:: 
    394 
    395            t1 = Table( 
    396                "my_table", 
    397                autoload_with=some_engine, 
    398                listeners=[("column_reflect", receive_column_reflect)], 
    399            ) 
    400 
    401        The dictionary of column information as returned by the 
    402        dialect is passed, and can be modified.  The dictionary 
    403        is that returned in each element of the list returned 
    404        by :meth:`.reflection.Inspector.get_columns`: 
    405 
    406            * ``name`` - the column's name, is applied to the 
    407              :paramref:`_schema.Column.name` parameter 
    408 
    409            * ``type`` - the type of this column, which should be an instance 
    410              of :class:`~sqlalchemy.types.TypeEngine`, is applied to the 
    411              :paramref:`_schema.Column.type` parameter 
    412 
    413            * ``nullable`` - boolean flag if the column is NULL or NOT NULL, 
    414              is applied to the :paramref:`_schema.Column.nullable` parameter 
    415 
    416            * ``default`` - the column's server default value.  This is 
    417              normally specified as a plain string SQL expression, however the 
    418              event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`, 
    419              or :func:`_expression.text` object as well.  Is applied to the 
    420              :paramref:`_schema.Column.server_default` parameter 
    421 
    422        The event is called before any action is taken against 
    423        this dictionary, and the contents can be modified; the following 
    424        additional keys may be added to the dictionary to further modify 
    425        how the :class:`_schema.Column` is constructed: 
    426 
    427 
    428            * ``key`` - the string key that will be used to access this 
    429              :class:`_schema.Column` in the ``.c`` collection; will be applied 
    430              to the :paramref:`_schema.Column.key` parameter. Is also used 
    431              for ORM mapping.  See the section 
    432              :ref:`mapper_automated_reflection_schemes` for an example. 
    433 
    434            * ``quote`` - force or un-force quoting on the column name; 
    435              is applied to the :paramref:`_schema.Column.quote` parameter. 
    436 
    437            * ``info`` - a dictionary of arbitrary data to follow along with 
    438              the :class:`_schema.Column`, is applied to the 
    439              :paramref:`_schema.Column.info` parameter. 
    440 
    441        :func:`.event.listen` also accepts the ``propagate=True`` 
    442        modifier for this event; when True, the listener function will 
    443        be established for any copies made of the target object, 
    444        i.e. those copies that are generated when 
    445        :meth:`_schema.Table.to_metadata` is used. 
    446 
    447        .. seealso:: 
    448 
    449            :ref:`mapper_automated_reflection_schemes` - 
    450            in the ORM mapping documentation 
    451 
    452            :ref:`automap_intercepting_columns` - 
    453            in the :ref:`automap_toplevel` documentation 
    454 
    455            :ref:`metadata_reflection_dbagnostic_types` - in 
    456            the :ref:`metadata_reflection_toplevel` documentation 
    457 
    458        """