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 """