1# pool/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
7from __future__ import annotations
8
9import typing
10from typing import Any
11from typing import Optional
12from typing import Type
13from typing import Union
14
15from .base import ConnectionPoolEntry
16from .base import Pool
17from .base import PoolProxiedConnection
18from .base import PoolResetState
19from .. import event
20from .. import util
21
22if typing.TYPE_CHECKING:
23 from ..engine import Engine
24 from ..engine.interfaces import DBAPIConnection
25
26
27class PoolEvents(event.Events[Pool]):
28 """Available events for :class:`_pool.Pool`.
29
30 The methods here define the name of an event as well
31 as the names of members that are passed to listener
32 functions.
33
34 e.g.::
35
36 from sqlalchemy import event
37
38
39 def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
40 "handle an on checkout event"
41
42
43 event.listen(Pool, "checkout", my_on_checkout)
44
45 In addition to accepting the :class:`_pool.Pool` class and
46 :class:`_pool.Pool` instances, :class:`_events.PoolEvents` also accepts
47 :class:`_engine.Engine` objects and the :class:`_engine.Engine` class as
48 targets, which will be resolved to the ``.pool`` attribute of the
49 given engine or the :class:`_pool.Pool` class::
50
51 engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
52
53 # will associate with engine.pool
54 event.listen(engine, "checkout", my_on_checkout)
55
56 """ # noqa: E501
57
58 _target_class_doc = "SomeEngineOrPool"
59 _dispatch_target = Pool
60
61 @util.preload_module("sqlalchemy.engine")
62 @classmethod
63 def _accept_with(
64 cls,
65 target: Union[Pool, Type[Pool], Engine, Type[Engine]],
66 identifier: str,
67 ) -> Optional[Union[Pool, Type[Pool]]]:
68 if not typing.TYPE_CHECKING:
69 Engine = util.preloaded.engine.Engine
70
71 if isinstance(target, type):
72 if issubclass(target, Engine):
73 return Pool
74 else:
75 assert issubclass(target, Pool)
76 return target
77 elif isinstance(target, Engine):
78 return target.pool
79 elif isinstance(target, Pool):
80 return target
81 elif hasattr(target, "_no_async_engine_events"):
82 target._no_async_engine_events()
83 else:
84 return None
85
86 @classmethod
87 def _listen(
88 cls,
89 event_key: event._EventKey[Pool],
90 **kw: Any,
91 ) -> None:
92 target = event_key.dispatch_target
93
94 kw.setdefault("asyncio", target._is_asyncio)
95
96 event_key.base_listen(**kw)
97
98 def connect(
99 self,
100 dbapi_connection: DBAPIConnection,
101 connection_record: ConnectionPoolEntry,
102 ) -> None:
103 """Called at the moment a particular DBAPI connection is first
104 created for a given :class:`_pool.Pool`.
105
106 This event allows one to capture the point directly after which
107 the DBAPI module-level ``.connect()`` method has been used in order
108 to produce a new DBAPI connection.
109
110 :param dbapi_connection: a DBAPI connection.
111 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
112
113 :param connection_record: the :class:`.ConnectionPoolEntry` managing
114 the DBAPI connection.
115
116 """
117
118 def first_connect(
119 self,
120 dbapi_connection: DBAPIConnection,
121 connection_record: ConnectionPoolEntry,
122 ) -> None:
123 """Called exactly once for the first time a DBAPI connection is
124 checked out from a particular :class:`_pool.Pool`.
125
126 The rationale for :meth:`_events.PoolEvents.first_connect`
127 is to determine
128 information about a particular series of database connections based
129 on the settings used for all connections. Since a particular
130 :class:`_pool.Pool`
131 refers to a single "creator" function (which in terms
132 of a :class:`_engine.Engine`
133 refers to the URL and connection options used),
134 it is typically valid to make observations about a single connection
135 that can be safely assumed to be valid about all subsequent
136 connections, such as the database version, the server and client
137 encoding settings, collation settings, and many others.
138
139 :param dbapi_connection: a DBAPI connection.
140 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
141
142 :param connection_record: the :class:`.ConnectionPoolEntry` managing
143 the DBAPI connection.
144
145 """
146
147 def checkout(
148 self,
149 dbapi_connection: DBAPIConnection,
150 connection_record: ConnectionPoolEntry,
151 connection_proxy: PoolProxiedConnection,
152 ) -> None:
153 """Called when a connection is retrieved from the Pool.
154
155 :param dbapi_connection: a DBAPI connection.
156 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
157
158 :param connection_record: the :class:`.ConnectionPoolEntry` managing
159 the DBAPI connection.
160
161 :param connection_proxy: the :class:`.PoolProxiedConnection` object
162 which will proxy the public interface of the DBAPI connection for the
163 lifespan of the checkout.
164
165 If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
166 connection will be disposed and a fresh connection retrieved.
167 Processing of all checkout listeners will abort and restart
168 using the new connection.
169
170 .. seealso:: :meth:`_events.ConnectionEvents.engine_connect`
171 - a similar event
172 which occurs upon creation of a new :class:`_engine.Connection`.
173
174 """
175
176 def checkin(
177 self,
178 dbapi_connection: Optional[DBAPIConnection],
179 connection_record: ConnectionPoolEntry,
180 ) -> None:
181 """Called when a connection returns to the pool.
182
183 Note that the connection may be closed, and may be None if the
184 connection has been invalidated. ``checkin`` will not be called
185 for detached connections. (They do not return to the pool.)
186
187 :param dbapi_connection: a DBAPI connection.
188 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
189
190 :param connection_record: the :class:`.ConnectionPoolEntry` managing
191 the DBAPI connection.
192
193 """
194
195 @event._legacy_signature(
196 "2.0",
197 ["dbapi_connection", "connection_record"],
198 lambda dbapi_connection, connection_record, reset_state: (
199 dbapi_connection,
200 connection_record,
201 ),
202 )
203 def reset(
204 self,
205 dbapi_connection: DBAPIConnection,
206 connection_record: ConnectionPoolEntry,
207 reset_state: PoolResetState,
208 ) -> None:
209 """Called before the "reset" action occurs for a pooled connection.
210
211 This event represents
212 when the ``rollback()`` method is called on the DBAPI connection
213 before it is returned to the pool or discarded.
214 A custom "reset" strategy may be implemented using this event hook,
215 which may also be combined with disabling the default "reset"
216 behavior using the :paramref:`_pool.Pool.reset_on_return` parameter.
217
218 The primary difference between the :meth:`_events.PoolEvents.reset` and
219 :meth:`_events.PoolEvents.checkin` events are that
220 :meth:`_events.PoolEvents.reset` is called not just for pooled
221 connections that are being returned to the pool, but also for
222 connections that were detached using the
223 :meth:`_engine.Connection.detach` method as well as asyncio connections
224 that are being discarded due to garbage collection taking place on
225 connections before the connection was checked in.
226
227 Note that the event **is not** invoked for connections that were
228 invalidated using :meth:`_engine.Connection.invalidate`. These
229 events may be intercepted using the :meth:`.PoolEvents.soft_invalidate`
230 and :meth:`.PoolEvents.invalidate` event hooks, and all "connection
231 close" events may be intercepted using :meth:`.PoolEvents.close`.
232
233 The :meth:`_events.PoolEvents.reset` event is usually followed by the
234 :meth:`_events.PoolEvents.checkin` event, except in those
235 cases where the connection is discarded immediately after reset.
236
237 :param dbapi_connection: a DBAPI connection.
238 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
239
240 :param connection_record: the :class:`.ConnectionPoolEntry` managing
241 the DBAPI connection.
242
243 :param reset_state: :class:`.PoolResetState` instance which provides
244 information about the circumstances under which the connection
245 is being reset.
246
247 .. versionadded:: 2.0
248
249 .. seealso::
250
251 :ref:`pool_reset_on_return`
252
253 :meth:`_events.ConnectionEvents.rollback`
254
255 :meth:`_events.ConnectionEvents.commit`
256
257 """
258
259 def invalidate(
260 self,
261 dbapi_connection: DBAPIConnection,
262 connection_record: ConnectionPoolEntry,
263 exception: Optional[BaseException],
264 ) -> None:
265 """Called when a DBAPI connection is to be "invalidated".
266
267 This event is called any time the
268 :meth:`.ConnectionPoolEntry.invalidate` method is invoked, either from
269 API usage or via "auto-invalidation", without the ``soft`` flag.
270
271 The event occurs before a final attempt to call ``.close()`` on the
272 connection occurs.
273
274 :param dbapi_connection: a DBAPI connection.
275 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
276
277 :param connection_record: the :class:`.ConnectionPoolEntry` managing
278 the DBAPI connection.
279
280 :param exception: the exception object corresponding to the reason
281 for this invalidation, if any. May be ``None``.
282
283 .. seealso::
284
285 :ref:`pool_connection_invalidation`
286
287 """
288
289 def soft_invalidate(
290 self,
291 dbapi_connection: DBAPIConnection,
292 connection_record: ConnectionPoolEntry,
293 exception: Optional[BaseException],
294 ) -> None:
295 """Called when a DBAPI connection is to be "soft invalidated".
296
297 This event is called any time the
298 :meth:`.ConnectionPoolEntry.invalidate`
299 method is invoked with the ``soft`` flag.
300
301 Soft invalidation refers to when the connection record that tracks
302 this connection will force a reconnect after the current connection
303 is checked in. It does not actively close the dbapi_connection
304 at the point at which it is called.
305
306 :param dbapi_connection: a DBAPI connection.
307 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
308
309 :param connection_record: the :class:`.ConnectionPoolEntry` managing
310 the DBAPI connection.
311
312 :param exception: the exception object corresponding to the reason
313 for this invalidation, if any. May be ``None``.
314
315 """
316
317 def close(
318 self,
319 dbapi_connection: DBAPIConnection,
320 connection_record: ConnectionPoolEntry,
321 ) -> None:
322 """Called when a DBAPI connection is closed.
323
324 The event is emitted before the close occurs.
325
326 The close of a connection can fail; typically this is because
327 the connection is already closed. If the close operation fails,
328 the connection is discarded.
329
330 The :meth:`.close` event corresponds to a connection that's still
331 associated with the pool. To intercept close events for detached
332 connections use :meth:`.close_detached`.
333
334 :param dbapi_connection: a DBAPI connection.
335 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
336
337 :param connection_record: the :class:`.ConnectionPoolEntry` managing
338 the DBAPI connection.
339
340 """
341
342 def detach(
343 self,
344 dbapi_connection: DBAPIConnection,
345 connection_record: ConnectionPoolEntry,
346 ) -> None:
347 """Called when a DBAPI connection is "detached" from a pool.
348
349 This event is emitted after the detach occurs. The connection
350 is no longer associated with the given connection record.
351
352 :param dbapi_connection: a DBAPI connection.
353 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
354
355 :param connection_record: the :class:`.ConnectionPoolEntry` managing
356 the DBAPI connection.
357
358 """
359
360 def close_detached(self, dbapi_connection: DBAPIConnection) -> None:
361 """Called when a detached DBAPI connection is closed.
362
363 The event is emitted before the close occurs.
364
365 The close of a connection can fail; typically this is because
366 the connection is already closed. If the close operation fails,
367 the connection is discarded.
368
369 :param dbapi_connection: a DBAPI connection.
370 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
371
372 """