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