1# engine/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
8
9from .base import Engine
10from .interfaces import Connectable
11from .interfaces import Dialect
12from .. import event
13from .. import exc
14
15
16class ConnectionEvents(event.Events):
17 """Available events for :class:`.Connectable`, which includes
18 :class:`_engine.Connection` and :class:`_engine.Engine`.
19
20 The methods here define the name of an event as well as the names of
21 members that are passed to listener functions.
22
23 An event listener can be associated with any :class:`.Connectable`
24 class or instance, such as an :class:`_engine.Engine`, e.g.::
25
26 from sqlalchemy import event, create_engine
27
28 def before_cursor_execute(conn, cursor, statement, parameters, context,
29 executemany):
30 log.info("Received statement: %s", statement)
31
32 engine = create_engine('postgresql://scott:tiger@localhost/test')
33 event.listen(engine, "before_cursor_execute", before_cursor_execute)
34
35 or with a specific :class:`_engine.Connection`::
36
37 with engine.begin() as conn:
38 @event.listens_for(conn, 'before_cursor_execute')
39 def before_cursor_execute(conn, cursor, statement, parameters,
40 context, executemany):
41 log.info("Received statement: %s", statement)
42
43 When the methods are called with a `statement` parameter, such as in
44 :meth:`.after_cursor_execute` or :meth:`.before_cursor_execute`,
45 the statement is the exact SQL string that was prepared for transmission
46 to the DBAPI ``cursor`` in the connection's :class:`.Dialect`.
47
48 The :meth:`.before_execute` and :meth:`.before_cursor_execute`
49 events can also be established with the ``retval=True`` flag, which
50 allows modification of the statement and parameters to be sent
51 to the database. The :meth:`.before_cursor_execute` event is
52 particularly useful here to add ad-hoc string transformations, such
53 as comments, to all executions::
54
55 from sqlalchemy.engine import Engine
56 from sqlalchemy import event
57
58 @event.listens_for(Engine, "before_cursor_execute", retval=True)
59 def comment_sql_calls(conn, cursor, statement, parameters,
60 context, executemany):
61 statement = statement + " -- some comment"
62 return statement, parameters
63
64 .. note:: :class:`_events.ConnectionEvents` can be established on any
65 combination of :class:`_engine.Engine`, :class:`_engine.Connection`,
66 as well
67 as instances of each of those classes. Events across all
68 four scopes will fire off for a given instance of
69 :class:`_engine.Connection`. However, for performance reasons, the
70 :class:`_engine.Connection` object determines at instantiation time
71 whether or not its parent :class:`_engine.Engine` has event listeners
72 established. Event listeners added to the :class:`_engine.Engine`
73 class or to an instance of :class:`_engine.Engine`
74 *after* the instantiation
75 of a dependent :class:`_engine.Connection` instance will usually
76 *not* be available on that :class:`_engine.Connection` instance.
77 The newly
78 added listeners will instead take effect for
79 :class:`_engine.Connection`
80 instances created subsequent to those event listeners being
81 established on the parent :class:`_engine.Engine` class or instance.
82
83 :param retval=False: Applies to the :meth:`.before_execute` and
84 :meth:`.before_cursor_execute` events only. When True, the
85 user-defined event function must have a return value, which
86 is a tuple of parameters that replace the given statement
87 and parameters. See those methods for a description of
88 specific return arguments.
89
90 """
91
92 _target_class_doc = "SomeEngine"
93 _dispatch_target = Connectable
94
95 @classmethod
96 def _listen(cls, event_key, retval=False):
97 target, identifier, fn = (
98 event_key.dispatch_target,
99 event_key.identifier,
100 event_key._listen_fn,
101 )
102
103 target._has_events = True
104
105 if not retval:
106 if identifier == "before_execute":
107 orig_fn = fn
108
109 def wrap_before_execute(
110 conn, clauseelement, multiparams, params, execution_options
111 ):
112 orig_fn(
113 conn,
114 clauseelement,
115 multiparams,
116 params,
117 execution_options,
118 )
119 return clauseelement, multiparams, params
120
121 fn = wrap_before_execute
122 elif identifier == "before_cursor_execute":
123 orig_fn = fn
124
125 def wrap_before_cursor_execute(
126 conn, cursor, statement, parameters, context, executemany
127 ):
128 orig_fn(
129 conn,
130 cursor,
131 statement,
132 parameters,
133 context,
134 executemany,
135 )
136 return statement, parameters
137
138 fn = wrap_before_cursor_execute
139 elif retval and identifier not in (
140 "before_execute",
141 "before_cursor_execute",
142 "handle_error",
143 ):
144 raise exc.ArgumentError(
145 "Only the 'before_execute', "
146 "'before_cursor_execute' and 'handle_error' engine "
147 "event listeners accept the 'retval=True' "
148 "argument."
149 )
150 event_key.with_wrapper(fn).base_listen()
151
152 @event._legacy_signature(
153 "1.4",
154 ["conn", "clauseelement", "multiparams", "params"],
155 lambda conn, clauseelement, multiparams, params, execution_options: (
156 conn,
157 clauseelement,
158 multiparams,
159 params,
160 ),
161 )
162 def before_execute(
163 self, conn, clauseelement, multiparams, params, execution_options
164 ):
165 """Intercept high level execute() events, receiving uncompiled
166 SQL constructs and other objects prior to rendering into SQL.
167
168 This event is good for debugging SQL compilation issues as well
169 as early manipulation of the parameters being sent to the database,
170 as the parameter lists will be in a consistent format here.
171
172 This event can be optionally established with the ``retval=True``
173 flag. The ``clauseelement``, ``multiparams``, and ``params``
174 arguments should be returned as a three-tuple in this case::
175
176 @event.listens_for(Engine, "before_execute", retval=True)
177 def before_execute(conn, clauseelement, multiparams, params):
178 # do something with clauseelement, multiparams, params
179 return clauseelement, multiparams, params
180
181 :param conn: :class:`_engine.Connection` object
182 :param clauseelement: SQL expression construct, :class:`.Compiled`
183 instance, or string statement passed to
184 :meth:`_engine.Connection.execute`.
185 :param multiparams: Multiple parameter sets, a list of dictionaries.
186 :param params: Single parameter set, a single dictionary.
187 :param execution_options: dictionary of execution
188 options passed along with the statement, if any. This is a merge
189 of all options that will be used, including those of the statement,
190 the connection, and those passed in to the method itself for
191 the 2.0 style of execution.
192
193 .. versionadded: 1.4
194
195 .. seealso::
196
197 :meth:`.before_cursor_execute`
198
199 """
200
201 @event._legacy_signature(
202 "1.4",
203 ["conn", "clauseelement", "multiparams", "params", "result"],
204 lambda conn, clauseelement, multiparams, params, execution_options, result: ( # noqa
205 conn,
206 clauseelement,
207 multiparams,
208 params,
209 result,
210 ),
211 )
212 def after_execute(
213 self,
214 conn,
215 clauseelement,
216 multiparams,
217 params,
218 execution_options,
219 result,
220 ):
221 """Intercept high level execute() events after execute.
222
223
224 :param conn: :class:`_engine.Connection` object
225 :param clauseelement: SQL expression construct, :class:`.Compiled`
226 instance, or string statement passed to
227 :meth:`_engine.Connection.execute`.
228 :param multiparams: Multiple parameter sets, a list of dictionaries.
229 :param params: Single parameter set, a single dictionary.
230 :param execution_options: dictionary of execution
231 options passed along with the statement, if any. This is a merge
232 of all options that will be used, including those of the statement,
233 the connection, and those passed in to the method itself for
234 the 2.0 style of execution.
235
236 .. versionadded: 1.4
237
238 :param result: :class:`_engine.CursorResult` generated by the
239 execution.
240
241 """
242
243 def before_cursor_execute(
244 self, conn, cursor, statement, parameters, context, executemany
245 ):
246 """Intercept low-level cursor execute() events before execution,
247 receiving the string SQL statement and DBAPI-specific parameter list to
248 be invoked against a cursor.
249
250 This event is a good choice for logging as well as late modifications
251 to the SQL string. It's less ideal for parameter modifications except
252 for those which are specific to a target backend.
253
254 This event can be optionally established with the ``retval=True``
255 flag. The ``statement`` and ``parameters`` arguments should be
256 returned as a two-tuple in this case::
257
258 @event.listens_for(Engine, "before_cursor_execute", retval=True)
259 def before_cursor_execute(conn, cursor, statement,
260 parameters, context, executemany):
261 # do something with statement, parameters
262 return statement, parameters
263
264 See the example at :class:`_events.ConnectionEvents`.
265
266 :param conn: :class:`_engine.Connection` object
267 :param cursor: DBAPI cursor object
268 :param statement: string SQL statement, as to be passed to the DBAPI
269 :param parameters: Dictionary, tuple, or list of parameters being
270 passed to the ``execute()`` or ``executemany()`` method of the
271 DBAPI ``cursor``. In some cases may be ``None``.
272 :param context: :class:`.ExecutionContext` object in use. May
273 be ``None``.
274 :param executemany: boolean, if ``True``, this is an ``executemany()``
275 call, if ``False``, this is an ``execute()`` call.
276
277 .. seealso::
278
279 :meth:`.before_execute`
280
281 :meth:`.after_cursor_execute`
282
283 """
284
285 def after_cursor_execute(
286 self, conn, cursor, statement, parameters, context, executemany
287 ):
288 """Intercept low-level cursor execute() events after execution.
289
290 :param conn: :class:`_engine.Connection` object
291 :param cursor: DBAPI cursor object. Will have results pending
292 if the statement was a SELECT, but these should not be consumed
293 as they will be needed by the :class:`_engine.CursorResult`.
294 :param statement: string SQL statement, as passed to the DBAPI
295 :param parameters: Dictionary, tuple, or list of parameters being
296 passed to the ``execute()`` or ``executemany()`` method of the
297 DBAPI ``cursor``. In some cases may be ``None``.
298 :param context: :class:`.ExecutionContext` object in use. May
299 be ``None``.
300 :param executemany: boolean, if ``True``, this is an ``executemany()``
301 call, if ``False``, this is an ``execute()`` call.
302
303 """
304
305 def handle_error(self, exception_context):
306 r"""Intercept all exceptions processed by the
307 :class:`_engine.Connection`.
308
309 This includes all exceptions emitted by the DBAPI as well as
310 within SQLAlchemy's statement invocation process, including
311 encoding errors and other statement validation errors. Other areas
312 in which the event is invoked include transaction begin and end,
313 result row fetching, cursor creation.
314
315 Note that :meth:`.handle_error` may support new kinds of exceptions
316 and new calling scenarios at *any time*. Code which uses this
317 event must expect new calling patterns to be present in minor
318 releases.
319
320 To support the wide variety of members that correspond to an exception,
321 as well as to allow extensibility of the event without backwards
322 incompatibility, the sole argument received is an instance of
323 :class:`.ExceptionContext`. This object contains data members
324 representing detail about the exception.
325
326 Use cases supported by this hook include:
327
328 * read-only, low-level exception handling for logging and
329 debugging purposes
330 * exception re-writing
331 * Establishing or disabling whether a connection or the owning
332 connection pool is invalidated or expired in response to a
333 specific exception [1]_.
334
335 The hook is called while the cursor from the failed operation
336 (if any) is still open and accessible. Special cleanup operations
337 can be called on this cursor; SQLAlchemy will attempt to close
338 this cursor subsequent to this hook being invoked. If the connection
339 is in "autocommit" mode, the transaction also remains open within
340 the scope of this hook; the rollback of the per-statement transaction
341 also occurs after the hook is called.
342
343 .. note::
344
345 .. [1] The pool "pre_ping" handler enabled using the
346 :paramref:`_sa.create_engine.pool_pre_ping` parameter does
347 **not** consult this event before deciding if the "ping"
348 returned false, as opposed to receiving an unhandled error.
349 For this use case, the :ref:`legacy recipe based on
350 engine_connect() may be used
351 <pool_disconnects_pessimistic_custom>`. A future API allow
352 more comprehensive customization of the "disconnect"
353 detection mechanism across all functions.
354
355 A handler function has two options for replacing
356 the SQLAlchemy-constructed exception into one that is user
357 defined. It can either raise this new exception directly, in
358 which case all further event listeners are bypassed and the
359 exception will be raised, after appropriate cleanup as taken
360 place::
361
362 @event.listens_for(Engine, "handle_error")
363 def handle_exception(context):
364 if isinstance(context.original_exception,
365 psycopg2.OperationalError) and \
366 "failed" in str(context.original_exception):
367 raise MySpecialException("failed operation")
368
369 .. warning:: Because the
370 :meth:`_events.ConnectionEvents.handle_error`
371 event specifically provides for exceptions to be re-thrown as
372 the ultimate exception raised by the failed statement,
373 **stack traces will be misleading** if the user-defined event
374 handler itself fails and throws an unexpected exception;
375 the stack trace may not illustrate the actual code line that
376 failed! It is advised to code carefully here and use
377 logging and/or inline debugging if unexpected exceptions are
378 occurring.
379
380 Alternatively, a "chained" style of event handling can be
381 used, by configuring the handler with the ``retval=True``
382 modifier and returning the new exception instance from the
383 function. In this case, event handling will continue onto the
384 next handler. The "chained" exception is available using
385 :attr:`.ExceptionContext.chained_exception`::
386
387 @event.listens_for(Engine, "handle_error", retval=True)
388 def handle_exception(context):
389 if context.chained_exception is not None and \
390 "special" in context.chained_exception.message:
391 return MySpecialException("failed",
392 cause=context.chained_exception)
393
394 Handlers that return ``None`` may be used within the chain; when
395 a handler returns ``None``, the previous exception instance,
396 if any, is maintained as the current exception that is passed onto the
397 next handler.
398
399 When a custom exception is raised or returned, SQLAlchemy raises
400 this new exception as-is, it is not wrapped by any SQLAlchemy
401 object. If the exception is not a subclass of
402 :class:`sqlalchemy.exc.StatementError`,
403 certain features may not be available; currently this includes
404 the ORM's feature of adding a detail hint about "autoflush" to
405 exceptions raised within the autoflush process.
406
407 :param context: an :class:`.ExceptionContext` object. See this
408 class for details on all available members.
409
410 .. versionadded:: 0.9.7 Added the
411 :meth:`_events.ConnectionEvents.handle_error` hook.
412
413 .. versionchanged:: 1.1 The :meth:`.handle_error` event will now
414 receive all exceptions that inherit from ``BaseException``,
415 including ``SystemExit`` and ``KeyboardInterrupt``. The setting for
416 :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case and
417 the default for
418 :attr:`.ExceptionContext.invalidate_pool_on_disconnect` is
419 ``False``.
420
421 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now
422 invoked when an :class:`_engine.Engine` fails during the initial
423 call to :meth:`_engine.Engine.connect`, as well as when a
424 :class:`_engine.Connection` object encounters an error during a
425 reconnect operation.
426
427 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is
428 not fired off when a dialect makes use of the
429 ``skip_user_error_events`` execution option. This is used
430 by dialects which intend to catch SQLAlchemy-specific exceptions
431 within specific operations, such as when the MySQL dialect detects
432 a table not present within the ``has_table()`` dialect method.
433 Prior to 1.0.0, code which implements :meth:`.handle_error` needs
434 to ensure that exceptions thrown in these scenarios are re-raised
435 without modification.
436
437 """
438
439 def engine_connect(self, conn, branch):
440 """Intercept the creation of a new :class:`_engine.Connection`.
441
442 This event is called typically as the direct result of calling
443 the :meth:`_engine.Engine.connect` method.
444
445 It differs from the :meth:`_events.PoolEvents.connect` method, which
446 refers to the actual connection to a database at the DBAPI level;
447 a DBAPI connection may be pooled and reused for many operations.
448 In contrast, this event refers only to the production of a higher level
449 :class:`_engine.Connection` wrapper around such a DBAPI connection.
450
451 It also differs from the :meth:`_events.PoolEvents.checkout` event
452 in that it is specific to the :class:`_engine.Connection` object,
453 not the
454 DBAPI connection that :meth:`_events.PoolEvents.checkout` deals with,
455 although
456 this DBAPI connection is available here via the
457 :attr:`_engine.Connection.connection` attribute.
458 But note there can in fact
459 be multiple :meth:`_events.PoolEvents.checkout`
460 events within the lifespan
461 of a single :class:`_engine.Connection` object, if that
462 :class:`_engine.Connection`
463 is invalidated and re-established. There can also be multiple
464 :class:`_engine.Connection`
465 objects generated for the same already-checked-out
466 DBAPI connection, in the case that a "branch" of a
467 :class:`_engine.Connection`
468 is produced.
469
470 :param conn: :class:`_engine.Connection` object.
471 :param branch: if True, this is a "branch" of an existing
472 :class:`_engine.Connection`. A branch is generated within the course
473 of a statement execution to invoke supplemental statements, most
474 typically to pre-execute a SELECT of a default value for the purposes
475 of an INSERT statement.
476
477 .. seealso::
478
479 :meth:`_events.PoolEvents.checkout`
480 the lower-level pool checkout event
481 for an individual DBAPI connection
482
483 """
484
485 def set_connection_execution_options(self, conn, opts):
486 """Intercept when the :meth:`_engine.Connection.execution_options`
487 method is called.
488
489 This method is called after the new :class:`_engine.Connection`
490 has been
491 produced, with the newly updated execution options collection, but
492 before the :class:`.Dialect` has acted upon any of those new options.
493
494 Note that this method is not called when a new
495 :class:`_engine.Connection`
496 is produced which is inheriting execution options from its parent
497 :class:`_engine.Engine`; to intercept this condition, use the
498 :meth:`_events.ConnectionEvents.engine_connect` event.
499
500 :param conn: The newly copied :class:`_engine.Connection` object
501
502 :param opts: dictionary of options that were passed to the
503 :meth:`_engine.Connection.execution_options` method.
504
505 .. versionadded:: 0.9.0
506
507 .. seealso::
508
509 :meth:`_events.ConnectionEvents.set_engine_execution_options`
510 - event
511 which is called when :meth:`_engine.Engine.execution_options`
512 is called.
513
514
515 """
516
517 def set_engine_execution_options(self, engine, opts):
518 """Intercept when the :meth:`_engine.Engine.execution_options`
519 method is called.
520
521 The :meth:`_engine.Engine.execution_options` method produces a shallow
522 copy of the :class:`_engine.Engine` which stores the new options.
523 That new
524 :class:`_engine.Engine` is passed here.
525 A particular application of this
526 method is to add a :meth:`_events.ConnectionEvents.engine_connect`
527 event
528 handler to the given :class:`_engine.Engine`
529 which will perform some per-
530 :class:`_engine.Connection` task specific to these execution options.
531
532 :param conn: The newly copied :class:`_engine.Engine` object
533
534 :param opts: dictionary of options that were passed to the
535 :meth:`_engine.Connection.execution_options` method.
536
537 .. versionadded:: 0.9.0
538
539 .. seealso::
540
541 :meth:`_events.ConnectionEvents.set_connection_execution_options`
542 - event
543 which is called when :meth:`_engine.Connection.execution_options`
544 is
545 called.
546
547 """
548
549 def engine_disposed(self, engine):
550 """Intercept when the :meth:`_engine.Engine.dispose` method is called.
551
552 The :meth:`_engine.Engine.dispose` method instructs the engine to
553 "dispose" of it's connection pool (e.g. :class:`_pool.Pool`), and
554 replaces it with a new one. Disposing of the old pool has the
555 effect that existing checked-in connections are closed. The new
556 pool does not establish any new connections until it is first used.
557
558 This event can be used to indicate that resources related to the
559 :class:`_engine.Engine` should also be cleaned up,
560 keeping in mind that the
561 :class:`_engine.Engine`
562 can still be used for new requests in which case
563 it re-acquires connection resources.
564
565 .. versionadded:: 1.0.5
566
567 """
568
569 def begin(self, conn):
570 """Intercept begin() events.
571
572 :param conn: :class:`_engine.Connection` object
573
574 """
575
576 def rollback(self, conn):
577 """Intercept rollback() events, as initiated by a
578 :class:`.Transaction`.
579
580 Note that the :class:`_pool.Pool` also "auto-rolls back"
581 a DBAPI connection upon checkin, if the ``reset_on_return``
582 flag is set to its default value of ``'rollback'``.
583 To intercept this
584 rollback, use the :meth:`_events.PoolEvents.reset` hook.
585
586 :param conn: :class:`_engine.Connection` object
587
588 .. seealso::
589
590 :meth:`_events.PoolEvents.reset`
591
592 """
593
594 def commit(self, conn):
595 """Intercept commit() events, as initiated by a
596 :class:`.Transaction`.
597
598 Note that the :class:`_pool.Pool` may also "auto-commit"
599 a DBAPI connection upon checkin, if the ``reset_on_return``
600 flag is set to the value ``'commit'``. To intercept this
601 commit, use the :meth:`_events.PoolEvents.reset` hook.
602
603 :param conn: :class:`_engine.Connection` object
604 """
605
606 def savepoint(self, conn, name):
607 """Intercept savepoint() events.
608
609 :param conn: :class:`_engine.Connection` object
610 :param name: specified name used for the savepoint.
611
612 """
613
614 def rollback_savepoint(self, conn, name, context):
615 """Intercept rollback_savepoint() events.
616
617 :param conn: :class:`_engine.Connection` object
618 :param name: specified name used for the savepoint.
619 :param context: not used
620
621 """
622 # TODO: deprecate "context"
623
624 def release_savepoint(self, conn, name, context):
625 """Intercept release_savepoint() events.
626
627 :param conn: :class:`_engine.Connection` object
628 :param name: specified name used for the savepoint.
629 :param context: not used
630
631 """
632 # TODO: deprecate "context"
633
634 def begin_twophase(self, conn, xid):
635 """Intercept begin_twophase() events.
636
637 :param conn: :class:`_engine.Connection` object
638 :param xid: two-phase XID identifier
639
640 """
641
642 def prepare_twophase(self, conn, xid):
643 """Intercept prepare_twophase() events.
644
645 :param conn: :class:`_engine.Connection` object
646 :param xid: two-phase XID identifier
647 """
648
649 def rollback_twophase(self, conn, xid, is_prepared):
650 """Intercept rollback_twophase() events.
651
652 :param conn: :class:`_engine.Connection` object
653 :param xid: two-phase XID identifier
654 :param is_prepared: boolean, indicates if
655 :meth:`.TwoPhaseTransaction.prepare` was called.
656
657 """
658
659 def commit_twophase(self, conn, xid, is_prepared):
660 """Intercept commit_twophase() events.
661
662 :param conn: :class:`_engine.Connection` object
663 :param xid: two-phase XID identifier
664 :param is_prepared: boolean, indicates if
665 :meth:`.TwoPhaseTransaction.prepare` was called.
666
667 """
668
669
670class DialectEvents(event.Events):
671 """event interface for execution-replacement functions.
672
673 These events allow direct instrumentation and replacement
674 of key dialect functions which interact with the DBAPI.
675
676 .. note::
677
678 :class:`.DialectEvents` hooks should be considered **semi-public**
679 and experimental.
680 These hooks are not for general use and are only for those situations
681 where intricate re-statement of DBAPI mechanics must be injected onto
682 an existing dialect. For general-use statement-interception events,
683 please use the :class:`_events.ConnectionEvents` interface.
684
685 .. seealso::
686
687 :meth:`_events.ConnectionEvents.before_cursor_execute`
688
689 :meth:`_events.ConnectionEvents.before_execute`
690
691 :meth:`_events.ConnectionEvents.after_cursor_execute`
692
693 :meth:`_events.ConnectionEvents.after_execute`
694
695
696 .. versionadded:: 0.9.4
697
698 """
699
700 _target_class_doc = "SomeEngine"
701 _dispatch_target = Dialect
702
703 @classmethod
704 def _listen(cls, event_key, retval=False):
705 target = event_key.dispatch_target
706
707 target._has_events = True
708 event_key.base_listen()
709
710 @classmethod
711 def _accept_with(cls, target):
712 if isinstance(target, type):
713 if issubclass(target, Engine):
714 return Dialect
715 elif issubclass(target, Dialect):
716 return target
717 elif isinstance(target, Engine):
718 return target.dialect
719 elif isinstance(target, Dialect):
720 return target
721 elif hasattr(target, "dispatch") and hasattr(
722 target.dispatch._events, "_no_async_engine_events"
723 ):
724 target.dispatch._events._no_async_engine_events()
725 else:
726 return None
727
728 def do_connect(self, dialect, conn_rec, cargs, cparams):
729 """Receive connection arguments before a connection is made.
730
731 This event is useful in that it allows the handler to manipulate the
732 cargs and/or cparams collections that control how the DBAPI
733 ``connect()`` function will be called. ``cargs`` will always be a
734 Python list that can be mutated in-place, and ``cparams`` a Python
735 dictionary that may also be mutated::
736
737 e = create_engine("postgresql+psycopg2://user@host/dbname")
738
739 @event.listens_for(e, 'do_connect')
740 def receive_do_connect(dialect, conn_rec, cargs, cparams):
741 cparams["password"] = "some_password"
742
743 The event hook may also be used to override the call to ``connect()``
744 entirely, by returning a non-``None`` DBAPI connection object::
745
746 e = create_engine("postgresql+psycopg2://user@host/dbname")
747
748 @event.listens_for(e, 'do_connect')
749 def receive_do_connect(dialect, conn_rec, cargs, cparams):
750 return psycopg2.connect(*cargs, **cparams)
751
752
753 .. versionadded:: 1.0.3
754
755 .. seealso::
756
757 :ref:`custom_dbapi_args`
758
759 """
760
761 def do_executemany(self, cursor, statement, parameters, context):
762 """Receive a cursor to have executemany() called.
763
764 Return the value True to halt further events from invoking,
765 and to indicate that the cursor execution has already taken
766 place within the event handler.
767
768 """
769
770 def do_execute_no_params(self, cursor, statement, context):
771 """Receive a cursor to have execute() with no parameters called.
772
773 Return the value True to halt further events from invoking,
774 and to indicate that the cursor execution has already taken
775 place within the event handler.
776
777 """
778
779 def do_execute(self, cursor, statement, parameters, context):
780 """Receive a cursor to have execute() called.
781
782 Return the value True to halt further events from invoking,
783 and to indicate that the cursor execution has already taken
784 place within the event handler.
785
786 """
787
788 def do_setinputsizes(
789 self, inputsizes, cursor, statement, parameters, context
790 ):
791 """Receive the setinputsizes dictionary for possible modification.
792
793 This event is emitted in the case where the dialect makes use of the
794 DBAPI ``cursor.setinputsizes()`` method which passes information about
795 parameter binding for a particular statement. The given
796 ``inputsizes`` dictionary will contain :class:`.BindParameter` objects
797 as keys, linked to DBAPI-specific type objects as values; for
798 parameters that are not bound, they are added to the dictionary with
799 ``None`` as the value, which means the parameter will not be included
800 in the ultimate setinputsizes call. The event may be used to inspect
801 and/or log the datatypes that are being bound, as well as to modify the
802 dictionary in place. Parameters can be added, modified, or removed
803 from this dictionary. Callers will typically want to inspect the
804 :attr:`.BindParameter.type` attribute of the given bind objects in
805 order to make decisions about the DBAPI object.
806
807 After the event, the ``inputsizes`` dictionary is converted into
808 an appropriate datastructure to be passed to ``cursor.setinputsizes``;
809 either a list for a positional bound parameter execution style,
810 or a dictionary of string parameter keys to DBAPI type objects for
811 a named bound parameter execution style.
812
813 The setinputsizes hook overall is only used for dialects which include
814 the flag ``use_setinputsizes=True``. Dialects which use this
815 include cx_Oracle, pg8000, asyncpg, and pyodbc dialects.
816
817 .. note::
818
819 For use with pyodbc, the ``use_setinputsizes`` flag
820 must be passed to the dialect, e.g.::
821
822 create_engine("mssql+pyodbc://...", use_setinputsizes=True)
823
824 .. seealso::
825
826 :ref:`mssql_pyodbc_setinputsizes`
827
828 .. versionadded:: 1.2.9
829
830 .. seealso::
831
832 :ref:`cx_oracle_setinputsizes`
833
834 """
835 pass