Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/engine/base.py : 40%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# engine/base.py
2# Copyright (C) 2005-2020 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: http://www.opensource.org/licenses/mit-license.php
7from __future__ import with_statement
9import contextlib
10import sys
12from .interfaces import Connectable
13from .interfaces import ExceptionContext
14from .util import _distill_params
15from .. import exc
16from .. import interfaces
17from .. import log
18from .. import util
19from ..sql import schema
20from ..sql import util as sql_util
23"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`.
25"""
28class Connection(Connectable):
29 """Provides high-level functionality for a wrapped DB-API connection.
31 Provides execution support for string-based SQL statements as well as
32 :class:`_expression.ClauseElement`, :class:`.Compiled` and
33 :class:`.DefaultGenerator`
34 objects. Provides a :meth:`begin` method to return :class:`.Transaction`
35 objects.
37 The Connection object is **not** thread-safe. While a Connection can be
38 shared among threads using properly synchronized access, it is still
39 possible that the underlying DBAPI connection may not support shared
40 access between threads. Check the DBAPI documentation for details.
42 The Connection object represents a single dbapi connection checked out
43 from the connection pool. In this state, the connection pool has no affect
44 upon the connection, including its expiration or timeout state. For the
45 connection pool to properly manage connections, connections should be
46 returned to the connection pool (i.e. ``connection.close()``) whenever the
47 connection is not in use.
49 .. index::
50 single: thread safety; Connection
52 """
54 schema_for_object = schema._schema_getter(None)
55 """Return the ".schema" attribute for an object.
57 Used for :class:`_schema.Table`, :class:`.Sequence` and similar objects,
58 and takes into account
59 the :paramref:`.Connection.execution_options.schema_translate_map`
60 parameter.
62 .. versionadded:: 1.1
64 .. seealso::
66 :ref:`schema_translating`
68 """
70 def __init__(
71 self,
72 engine,
73 connection=None,
74 close_with_result=False,
75 _branch_from=None,
76 _execution_options=None,
77 _dispatch=None,
78 _has_events=None,
79 ):
80 """Construct a new Connection.
82 The constructor here is not public and is only called only by an
83 :class:`_engine.Engine`. See :meth:`_engine.Engine.connect` and
84 :meth:`_engine.Engine.contextual_connect` methods.
86 """
87 self.engine = engine
88 self.dialect = engine.dialect
89 self.__branch_from = _branch_from
90 self.__branch = _branch_from is not None
92 if _branch_from:
93 self.__connection = connection
94 self._execution_options = _execution_options
95 self._echo = _branch_from._echo
96 self.should_close_with_result = False
97 self.dispatch = _dispatch
98 self._has_events = _branch_from._has_events
99 self.schema_for_object = _branch_from.schema_for_object
100 else:
101 self.__connection = (
102 connection
103 if connection is not None
104 else engine.raw_connection()
105 )
106 self.__transaction = None
107 self.__savepoint_seq = 0
108 self.should_close_with_result = close_with_result
109 self.__invalid = False
110 self.__can_reconnect = True
111 self._echo = self.engine._should_log_info()
113 if _has_events is None:
114 # if _has_events is sent explicitly as False,
115 # then don't join the dispatch of the engine; we don't
116 # want to handle any of the engine's events in that case.
117 self.dispatch = self.dispatch._join(engine.dispatch)
118 self._has_events = _has_events or (
119 _has_events is None and engine._has_events
120 )
122 assert not _execution_options
123 self._execution_options = engine._execution_options
125 if self._has_events or self.engine._has_events:
126 self.dispatch.engine_connect(self, self.__branch)
128 def _branch(self):
129 """Return a new Connection which references this Connection's
130 engine and connection; but does not have close_with_result enabled,
131 and also whose close() method does nothing.
133 The Core uses this very sparingly, only in the case of
134 custom SQL default functions that are to be INSERTed as the
135 primary key of a row where we need to get the value back, so we have
136 to invoke it distinctly - this is a very uncommon case.
138 Userland code accesses _branch() when the connect() or
139 contextual_connect() methods are called. The branched connection
140 acts as much as possible like the parent, except that it stays
141 connected when a close() event occurs.
143 """
144 if self.__branch_from:
145 return self.__branch_from._branch()
146 else:
147 return self.engine._connection_cls(
148 self.engine,
149 self.__connection,
150 _branch_from=self,
151 _execution_options=self._execution_options,
152 _has_events=self._has_events,
153 _dispatch=self.dispatch,
154 )
156 @property
157 def _root(self):
158 """return the 'root' connection.
160 Returns 'self' if this connection is not a branch, else
161 returns the root connection from which we ultimately branched.
163 """
165 if self.__branch_from:
166 return self.__branch_from
167 else:
168 return self
170 def _clone(self):
171 """Create a shallow copy of this Connection.
173 """
174 c = self.__class__.__new__(self.__class__)
175 c.__dict__ = self.__dict__.copy()
176 return c
178 def __enter__(self):
179 return self
181 def __exit__(self, type_, value, traceback):
182 self.close()
184 def execution_options(self, **opt):
185 r""" Set non-SQL options for the connection which take effect
186 during execution.
188 The method returns a copy of this :class:`_engine.Connection`
189 which references
190 the same underlying DBAPI connection, but also defines the given
191 execution options which will take effect for a call to
192 :meth:`execute`. As the new :class:`_engine.Connection`
193 references the same
194 underlying resource, it's usually a good idea to ensure that the copies
195 will be discarded immediately, which is implicit if used as in::
197 result = connection.execution_options(stream_results=True).\
198 execute(stmt)
200 Note that any key/value can be passed to
201 :meth:`_engine.Connection.execution_options`,
202 and it will be stored in the
203 ``_execution_options`` dictionary of the :class:`_engine.Connection`.
204 It
205 is suitable for usage by end-user schemes to communicate with
206 event listeners, for example.
208 The keywords that are currently recognized by SQLAlchemy itself
209 include all those listed under :meth:`.Executable.execution_options`,
210 as well as others that are specific to :class:`_engine.Connection`.
212 :param autocommit: Available on: Connection, statement.
213 When True, a COMMIT will be invoked after execution
214 when executed in 'autocommit' mode, i.e. when an explicit
215 transaction is not begun on the connection. Note that DBAPI
216 connections by default are always in a transaction - SQLAlchemy uses
217 rules applied to different kinds of statements to determine if
218 COMMIT will be invoked in order to provide its "autocommit" feature.
219 Typically, all INSERT/UPDATE/DELETE statements as well as
220 CREATE/DROP statements have autocommit behavior enabled; SELECT
221 constructs do not. Use this option when invoking a SELECT or other
222 specific SQL construct where COMMIT is desired (typically when
223 calling stored procedures and such), and an explicit
224 transaction is not in progress.
226 :param compiled_cache: Available on: Connection.
227 A dictionary where :class:`.Compiled` objects
228 will be cached when the :class:`_engine.Connection`
229 compiles a clause
230 expression into a :class:`.Compiled` object.
231 It is the user's responsibility to
232 manage the size of this dictionary, which will have keys
233 corresponding to the dialect, clause element, the column
234 names within the VALUES or SET clause of an INSERT or UPDATE,
235 as well as the "batch" mode for an INSERT or UPDATE statement.
236 The format of this dictionary is not guaranteed to stay the
237 same in future releases.
239 Note that the ORM makes use of its own "compiled" caches for
240 some operations, including flush operations. The caching
241 used by the ORM internally supersedes a cache dictionary
242 specified here.
244 :param isolation_level: Available on: :class:`_engine.Connection`.
246 Set the transaction isolation level for the lifespan of this
247 :class:`_engine.Connection` object.
248 Valid values include those string
249 values accepted by the :paramref:`_sa.create_engine.isolation_level`
250 parameter passed to :func:`_sa.create_engine`. These levels are
251 semi-database specific; see individual dialect documentation for
252 valid levels.
254 The isolation level option applies the isolation level by emitting
255 statements on the DBAPI connection, and **necessarily affects the
256 original Connection object overall**, not just the copy that is
257 returned by the call to :meth:`_engine.Connection.execution_options`
258 method. The isolation level will remain at the given setting until
259 the DBAPI connection itself is returned to the connection pool, i.e.
260 the :meth:`_engine.Connection.close` method on the original
261 :class:`_engine.Connection` is called,
262 where an event handler will emit
263 additional statements on the DBAPI connection in order to revert the
264 isolation level change.
266 .. warning:: The ``isolation_level`` execution option should
267 **not** be used when a transaction is already established, that
268 is, the :meth:`_engine.Connection.begin`
269 method or similar has been
270 called. A database cannot change the isolation level on a
271 transaction in progress, and different DBAPIs and/or
272 SQLAlchemy dialects may implicitly roll back or commit
273 the transaction, or not affect the connection at all.
275 .. note:: The ``isolation_level`` execution option is implicitly
276 reset if the :class:`_engine.Connection` is invalidated, e.g. via
277 the :meth:`_engine.Connection.invalidate` method, or if a
278 disconnection error occurs. The new connection produced after
279 the invalidation will not have the isolation level re-applied
280 to it automatically.
282 .. seealso::
284 :paramref:`_sa.create_engine.isolation_level`
285 - set per :class:`_engine.Engine` isolation level
287 :meth:`_engine.Connection.get_isolation_level`
288 - view current level
290 :ref:`SQLite Transaction Isolation <sqlite_isolation_level>`
292 :ref:`PostgreSQL Transaction Isolation <postgresql_isolation_level>`
294 :ref:`MySQL Transaction Isolation <mysql_isolation_level>`
296 :ref:`SQL Server Transaction Isolation <mssql_isolation_level>`
298 :ref:`session_transaction_isolation` - for the ORM
300 :param no_parameters: When ``True``, if the final parameter
301 list or dictionary is totally empty, will invoke the
302 statement on the cursor as ``cursor.execute(statement)``,
303 not passing the parameter collection at all.
304 Some DBAPIs such as psycopg2 and mysql-python consider
305 percent signs as significant only when parameters are
306 present; this option allows code to generate SQL
307 containing percent signs (and possibly other characters)
308 that is neutral regarding whether it's executed by the DBAPI
309 or piped into a script that's later invoked by
310 command line tools.
312 :param stream_results: Available on: Connection, statement.
313 Indicate to the dialect that results should be
314 "streamed" and not pre-buffered, if possible. This is a limitation
315 of many DBAPIs. The flag is currently understood only by the
316 psycopg2, mysqldb and pymysql dialects.
318 :param schema_translate_map: Available on: Connection, Engine.
319 A dictionary mapping schema names to schema names, that will be
320 applied to the :paramref:`_schema.Table.schema` element of each
321 :class:`_schema.Table`
322 encountered when SQL or DDL expression elements
323 are compiled into strings; the resulting schema name will be
324 converted based on presence in the map of the original name.
326 .. versionadded:: 1.1
328 .. seealso::
330 :ref:`schema_translating`
332 .. seealso::
334 :meth:`_engine.Engine.execution_options`
336 :meth:`.Executable.execution_options`
338 :meth:`_engine.Connection.get_execution_options`
341 """ # noqa
342 c = self._clone()
343 c._execution_options = c._execution_options.union(opt)
344 if self._has_events or self.engine._has_events:
345 self.dispatch.set_connection_execution_options(c, opt)
346 self.dialect.set_connection_execution_options(c, opt)
347 return c
349 def get_execution_options(self):
350 """ Get the non-SQL options which will take effect during execution.
352 .. versionadded:: 1.3
354 .. seealso::
356 :meth:`_engine.Connection.execution_options`
357 """
358 return self._execution_options
360 @property
361 def closed(self):
362 """Return True if this connection is closed."""
364 return (
365 "_Connection__connection" not in self.__dict__
366 and not self.__can_reconnect
367 )
369 @property
370 def invalidated(self):
371 """Return True if this connection was invalidated."""
373 return self._root.__invalid
375 @property
376 def connection(self):
377 """The underlying DB-API connection managed by this Connection.
379 .. seealso::
382 :ref:`dbapi_connections`
384 """
386 try:
387 return self.__connection
388 except AttributeError:
389 # escape "except AttributeError" before revalidating
390 # to prevent misleading stacktraces in Py3K
391 pass
392 try:
393 return self._revalidate_connection()
394 except BaseException as e:
395 self._handle_dbapi_exception(e, None, None, None, None)
397 def get_isolation_level(self):
398 """Return the current isolation level assigned to this
399 :class:`_engine.Connection`.
401 This will typically be the default isolation level as determined
402 by the dialect, unless if the
403 :paramref:`.Connection.execution_options.isolation_level`
404 feature has been used to alter the isolation level on a
405 per-:class:`_engine.Connection` basis.
407 This attribute will typically perform a live SQL operation in order
408 to procure the current isolation level, so the value returned is the
409 actual level on the underlying DBAPI connection regardless of how
410 this state was set. Compare to the
411 :attr:`_engine.Connection.default_isolation_level` accessor
412 which returns the dialect-level setting without performing a SQL
413 query.
415 .. versionadded:: 0.9.9
417 .. seealso::
419 :attr:`_engine.Connection.default_isolation_level`
420 - view default level
422 :paramref:`_sa.create_engine.isolation_level`
423 - set per :class:`_engine.Engine` isolation level
425 :paramref:`.Connection.execution_options.isolation_level`
426 - set per :class:`_engine.Connection` isolation level
428 """
429 try:
430 return self.dialect.get_isolation_level(self.connection)
431 except BaseException as e:
432 self._handle_dbapi_exception(e, None, None, None, None)
434 @property
435 def default_isolation_level(self):
436 """The default isolation level assigned to this
437 :class:`_engine.Connection`.
439 This is the isolation level setting that the
440 :class:`_engine.Connection`
441 has when first procured via the :meth:`_engine.Engine.connect` method.
442 This level stays in place until the
443 :paramref:`.Connection.execution_options.isolation_level` is used
444 to change the setting on a per-:class:`_engine.Connection` basis.
446 Unlike :meth:`_engine.Connection.get_isolation_level`,
447 this attribute is set
448 ahead of time from the first connection procured by the dialect,
449 so SQL query is not invoked when this accessor is called.
451 .. versionadded:: 0.9.9
453 .. seealso::
455 :meth:`_engine.Connection.get_isolation_level`
456 - view current level
458 :paramref:`_sa.create_engine.isolation_level`
459 - set per :class:`_engine.Engine` isolation level
461 :paramref:`.Connection.execution_options.isolation_level`
462 - set per :class:`_engine.Connection` isolation level
464 """
465 return self.dialect.default_isolation_level
467 def _revalidate_connection(self):
468 if self.__branch_from:
469 return self.__branch_from._revalidate_connection()
470 if self.__can_reconnect and self.__invalid:
471 if self.__transaction is not None:
472 raise exc.InvalidRequestError(
473 "Can't reconnect until invalid "
474 "transaction is rolled back"
475 )
476 self.__connection = self.engine.raw_connection(_connection=self)
477 self.__invalid = False
478 return self.__connection
479 raise exc.ResourceClosedError("This Connection is closed")
481 @property
482 def _connection_is_valid(self):
483 # use getattr() for is_valid to support exceptions raised in
484 # dialect initializer, where the connection is not wrapped in
485 # _ConnectionFairy
487 return getattr(self.__connection, "is_valid", False)
489 @property
490 def _still_open_and_connection_is_valid(self):
491 return (
492 not self.closed
493 and not self.invalidated
494 and getattr(self.__connection, "is_valid", False)
495 )
497 @property
498 def info(self):
499 """Info dictionary associated with the underlying DBAPI connection
500 referred to by this :class:`_engine.Connection`, allowing user-defined
501 data to be associated with the connection.
503 The data here will follow along with the DBAPI connection including
504 after it is returned to the connection pool and used again
505 in subsequent instances of :class:`_engine.Connection`.
507 """
509 return self.connection.info
511 def connect(self):
512 """Returns a branched version of this :class:`_engine.Connection`.
514 The :meth:`_engine.Connection.close` method on the returned
515 :class:`_engine.Connection` can be called and this
516 :class:`_engine.Connection` will remain open.
518 This method provides usage symmetry with
519 :meth:`_engine.Engine.connect`, including for usage
520 with context managers.
522 """
524 return self._branch()
526 def _contextual_connect(self, **kwargs):
527 return self._branch()
529 def invalidate(self, exception=None):
530 """Invalidate the underlying DBAPI connection associated with
531 this :class:`_engine.Connection`.
533 The underlying DBAPI connection is literally closed (if
534 possible), and is discarded. Its source connection pool will
535 typically lazily create a new connection to replace it.
537 Upon the next use (where "use" typically means using the
538 :meth:`_engine.Connection.execute` method or similar),
539 this :class:`_engine.Connection` will attempt to
540 procure a new DBAPI connection using the services of the
541 :class:`_pool.Pool` as a source of connectivity (e.g.
542 a "reconnection").
544 If a transaction was in progress (e.g. the
545 :meth:`_engine.Connection.begin` method has been called) when
546 :meth:`_engine.Connection.invalidate` method is called, at the DBAPI
547 level all state associated with this transaction is lost, as
548 the DBAPI connection is closed. The :class:`_engine.Connection`
549 will not allow a reconnection to proceed until the
550 :class:`.Transaction` object is ended, by calling the
551 :meth:`.Transaction.rollback` method; until that point, any attempt at
552 continuing to use the :class:`_engine.Connection` will raise an
553 :class:`~sqlalchemy.exc.InvalidRequestError`.
554 This is to prevent applications from accidentally
555 continuing an ongoing transactional operations despite the
556 fact that the transaction has been lost due to an
557 invalidation.
559 The :meth:`_engine.Connection.invalidate` method,
560 just like auto-invalidation,
561 will at the connection pool level invoke the
562 :meth:`_events.PoolEvents.invalidate` event.
564 .. seealso::
566 :ref:`pool_connection_invalidation`
568 """
570 if self.invalidated:
571 return
573 if self.closed:
574 raise exc.ResourceClosedError("This Connection is closed")
576 if self._root._connection_is_valid:
577 self._root.__connection.invalidate(exception)
578 del self._root.__connection
579 self._root.__invalid = True
581 def detach(self):
582 """Detach the underlying DB-API connection from its connection pool.
584 E.g.::
586 with engine.connect() as conn:
587 conn.detach()
588 conn.execute("SET search_path TO schema1, schema2")
590 # work with connection
592 # connection is fully closed (since we used "with:", can
593 # also call .close())
595 This :class:`_engine.Connection` instance will remain usable.
596 When closed
597 (or exited from a context manager context as above),
598 the DB-API connection will be literally closed and not
599 returned to its originating pool.
601 This method can be used to insulate the rest of an application
602 from a modified state on a connection (such as a transaction
603 isolation level or similar).
605 """
607 self.__connection.detach()
609 def begin(self):
610 """Begin a transaction and return a transaction handle.
612 The returned object is an instance of :class:`.Transaction`.
613 This object represents the "scope" of the transaction,
614 which completes when either the :meth:`.Transaction.rollback`
615 or :meth:`.Transaction.commit` method is called.
617 Nested calls to :meth:`.begin` on the same :class:`_engine.Connection`
618 will return new :class:`.Transaction` objects that represent
619 an emulated transaction within the scope of the enclosing
620 transaction, that is::
622 trans = conn.begin() # outermost transaction
623 trans2 = conn.begin() # "nested"
624 trans2.commit() # does nothing
625 trans.commit() # actually commits
627 Calls to :meth:`.Transaction.commit` only have an effect
628 when invoked via the outermost :class:`.Transaction` object, though the
629 :meth:`.Transaction.rollback` method of any of the
630 :class:`.Transaction` objects will roll back the
631 transaction.
633 .. seealso::
635 :meth:`_engine.Connection.begin_nested` - use a SAVEPOINT
637 :meth:`_engine.Connection.begin_twophase` -
638 use a two phase /XID transaction
640 :meth:`_engine.Engine.begin` - context manager available from
641 :class:`_engine.Engine`
643 """
644 if self.__branch_from:
645 return self.__branch_from.begin()
647 if self.__transaction is None:
648 self.__transaction = RootTransaction(self)
649 return self.__transaction
650 else:
651 return Transaction(self, self.__transaction)
653 def begin_nested(self):
654 """Begin a nested transaction and return a transaction handle.
656 The returned object is an instance of :class:`.NestedTransaction`.
658 Nested transactions require SAVEPOINT support in the
659 underlying database. Any transaction in the hierarchy may
660 ``commit`` and ``rollback``, however the outermost transaction
661 still controls the overall ``commit`` or ``rollback`` of the
662 transaction of a whole.
664 .. seealso::
666 :meth:`_engine.Connection.begin`
668 :meth:`_engine.Connection.begin_twophase`
670 """
671 if self.__branch_from:
672 return self.__branch_from.begin_nested()
674 if self.__transaction is None:
675 self.__transaction = RootTransaction(self)
676 else:
677 self.__transaction = NestedTransaction(self, self.__transaction)
678 return self.__transaction
680 def begin_twophase(self, xid=None):
681 """Begin a two-phase or XA transaction and return a transaction
682 handle.
684 The returned object is an instance of :class:`.TwoPhaseTransaction`,
685 which in addition to the methods provided by
686 :class:`.Transaction`, also provides a
687 :meth:`~.TwoPhaseTransaction.prepare` method.
689 :param xid: the two phase transaction id. If not supplied, a
690 random id will be generated.
692 .. seealso::
694 :meth:`_engine.Connection.begin`
696 :meth:`_engine.Connection.begin_twophase`
698 """
700 if self.__branch_from:
701 return self.__branch_from.begin_twophase(xid=xid)
703 if self.__transaction is not None:
704 raise exc.InvalidRequestError(
705 "Cannot start a two phase transaction when a transaction "
706 "is already in progress."
707 )
708 if xid is None:
709 xid = self.engine.dialect.create_xid()
710 self.__transaction = TwoPhaseTransaction(self, xid)
711 return self.__transaction
713 def recover_twophase(self):
714 return self.engine.dialect.do_recover_twophase(self)
716 def rollback_prepared(self, xid, recover=False):
717 self.engine.dialect.do_rollback_twophase(self, xid, recover=recover)
719 def commit_prepared(self, xid, recover=False):
720 self.engine.dialect.do_commit_twophase(self, xid, recover=recover)
722 def in_transaction(self):
723 """Return True if a transaction is in progress."""
724 return self._root.__transaction is not None
726 def _begin_impl(self, transaction):
727 assert not self.__branch_from
729 if self._echo:
730 self.engine.logger.info("BEGIN (implicit)")
732 if self._has_events or self.engine._has_events:
733 self.dispatch.begin(self)
735 try:
736 self.engine.dialect.do_begin(self.connection)
737 if self.connection._reset_agent is None:
738 self.connection._reset_agent = transaction
739 except BaseException as e:
740 self._handle_dbapi_exception(e, None, None, None, None)
742 def _rollback_impl(self):
743 assert not self.__branch_from
745 if self._has_events or self.engine._has_events:
746 self.dispatch.rollback(self)
748 if self._still_open_and_connection_is_valid:
749 if self._echo:
750 self.engine.logger.info("ROLLBACK")
751 try:
752 self.engine.dialect.do_rollback(self.connection)
753 except BaseException as e:
754 self._handle_dbapi_exception(e, None, None, None, None)
755 finally:
756 if (
757 not self.__invalid
758 and self.connection._reset_agent is self.__transaction
759 ):
760 self.connection._reset_agent = None
761 self.__transaction = None
762 else:
763 self.__transaction = None
765 def _commit_impl(self, autocommit=False):
766 assert not self.__branch_from
768 if self._has_events or self.engine._has_events:
769 self.dispatch.commit(self)
771 if self._echo:
772 self.engine.logger.info("COMMIT")
773 try:
774 self.engine.dialect.do_commit(self.connection)
775 except BaseException as e:
776 self._handle_dbapi_exception(e, None, None, None, None)
777 finally:
778 if (
779 not self.__invalid
780 and self.connection._reset_agent is self.__transaction
781 ):
782 self.connection._reset_agent = None
783 self.__transaction = None
785 def _savepoint_impl(self, name=None):
786 assert not self.__branch_from
788 if self._has_events or self.engine._has_events:
789 self.dispatch.savepoint(self, name)
791 if name is None:
792 self.__savepoint_seq += 1
793 name = "sa_savepoint_%s" % self.__savepoint_seq
794 if self._still_open_and_connection_is_valid:
795 self.engine.dialect.do_savepoint(self, name)
796 return name
798 def _discard_transaction(self, trans):
799 if trans is self.__transaction:
800 if trans._parent is trans:
801 self.__transaction = None
802 else:
803 self.__transaction = trans._parent
805 if self._still_open_and_connection_is_valid:
806 if self.__connection._reset_agent is trans:
807 self.__connection._reset_agent = None
809 def _rollback_to_savepoint_impl(self, name, context):
810 assert not self.__branch_from
812 if self._has_events or self.engine._has_events:
813 self.dispatch.rollback_savepoint(self, name, context)
815 if self._still_open_and_connection_is_valid:
816 self.engine.dialect.do_rollback_to_savepoint(self, name)
817 self.__transaction = context
819 def _release_savepoint_impl(self, name, context):
820 assert not self.__branch_from
822 if self._has_events or self.engine._has_events:
823 self.dispatch.release_savepoint(self, name, context)
825 if self._still_open_and_connection_is_valid:
826 self.engine.dialect.do_release_savepoint(self, name)
827 self.__transaction = context
829 def _begin_twophase_impl(self, transaction):
830 assert not self.__branch_from
832 if self._echo:
833 self.engine.logger.info("BEGIN TWOPHASE (implicit)")
834 if self._has_events or self.engine._has_events:
835 self.dispatch.begin_twophase(self, transaction.xid)
837 if self._still_open_and_connection_is_valid:
838 self.engine.dialect.do_begin_twophase(self, transaction.xid)
840 if self.connection._reset_agent is None:
841 self.connection._reset_agent = transaction
843 def _prepare_twophase_impl(self, xid):
844 assert not self.__branch_from
846 if self._has_events or self.engine._has_events:
847 self.dispatch.prepare_twophase(self, xid)
849 if self._still_open_and_connection_is_valid:
850 assert isinstance(self.__transaction, TwoPhaseTransaction)
851 self.engine.dialect.do_prepare_twophase(self, xid)
853 def _rollback_twophase_impl(self, xid, is_prepared):
854 assert not self.__branch_from
856 if self._has_events or self.engine._has_events:
857 self.dispatch.rollback_twophase(self, xid, is_prepared)
859 if self._still_open_and_connection_is_valid:
860 assert isinstance(self.__transaction, TwoPhaseTransaction)
861 try:
862 self.engine.dialect.do_rollback_twophase(
863 self, xid, is_prepared
864 )
865 finally:
866 if self.connection._reset_agent is self.__transaction:
867 self.connection._reset_agent = None
868 self.__transaction = None
869 else:
870 self.__transaction = None
872 def _commit_twophase_impl(self, xid, is_prepared):
873 assert not self.__branch_from
875 if self._has_events or self.engine._has_events:
876 self.dispatch.commit_twophase(self, xid, is_prepared)
878 if self._still_open_and_connection_is_valid:
879 assert isinstance(self.__transaction, TwoPhaseTransaction)
880 try:
881 self.engine.dialect.do_commit_twophase(self, xid, is_prepared)
882 finally:
883 if self.connection._reset_agent is self.__transaction:
884 self.connection._reset_agent = None
885 self.__transaction = None
886 else:
887 self.__transaction = None
889 def _autorollback(self):
890 if not self._root.in_transaction():
891 self._root._rollback_impl()
893 def close(self):
894 """Close this :class:`_engine.Connection`.
896 This results in a release of the underlying database
897 resources, that is, the DBAPI connection referenced
898 internally. The DBAPI connection is typically restored
899 back to the connection-holding :class:`_pool.Pool` referenced
900 by the :class:`_engine.Engine` that produced this
901 :class:`_engine.Connection`. Any transactional state present on
902 the DBAPI connection is also unconditionally released via
903 the DBAPI connection's ``rollback()`` method, regardless
904 of any :class:`.Transaction` object that may be
905 outstanding with regards to this :class:`_engine.Connection`.
907 After :meth:`_engine.Connection.close` is called, the
908 :class:`_engine.Connection` is permanently in a closed state,
909 and will allow no further operations.
911 """
912 if self.__branch_from:
913 try:
914 del self.__connection
915 except AttributeError:
916 pass
917 finally:
918 self.__can_reconnect = False
919 return
920 try:
921 conn = self.__connection
922 except AttributeError:
923 pass
924 else:
926 conn.close()
927 if conn._reset_agent is self.__transaction:
928 conn._reset_agent = None
930 # the close() process can end up invalidating us,
931 # as the pool will call our transaction as the "reset_agent"
932 # for rollback(), which can then cause an invalidation
933 if not self.__invalid:
934 del self.__connection
935 self.__can_reconnect = False
936 self.__transaction = None
938 def scalar(self, object_, *multiparams, **params):
939 """Executes and returns the first column of the first row.
941 The underlying result/cursor is closed after execution.
942 """
944 return self.execute(object_, *multiparams, **params).scalar()
946 def execute(self, object_, *multiparams, **params):
947 r"""Executes a SQL statement construct and returns a
948 :class:`_engine.ResultProxy`.
950 :param object: The statement to be executed. May be
951 one of:
953 * a plain string
954 * any :class:`_expression.ClauseElement` construct that is also
955 a subclass of :class:`.Executable`, such as a
956 :func:`_expression.select` construct
957 * a :class:`.FunctionElement`, such as that generated
958 by :data:`.func`, will be automatically wrapped in
959 a SELECT statement, which is then executed.
960 * a :class:`.DDLElement` object
961 * a :class:`.DefaultGenerator` object
962 * a :class:`.Compiled` object
964 :param \*multiparams/\**params: represent bound parameter
965 values to be used in the execution. Typically,
966 the format is either a collection of one or more
967 dictionaries passed to \*multiparams::
969 conn.execute(
970 table.insert(),
971 {"id":1, "value":"v1"},
972 {"id":2, "value":"v2"}
973 )
975 ...or individual key/values interpreted by \**params::
977 conn.execute(
978 table.insert(), id=1, value="v1"
979 )
981 In the case that a plain SQL string is passed, and the underlying
982 DBAPI accepts positional bind parameters, a collection of tuples
983 or individual values in \*multiparams may be passed::
985 conn.execute(
986 "INSERT INTO table (id, value) VALUES (?, ?)",
987 (1, "v1"), (2, "v2")
988 )
990 conn.execute(
991 "INSERT INTO table (id, value) VALUES (?, ?)",
992 1, "v1"
993 )
995 Note above, the usage of a question mark "?" or other
996 symbol is contingent upon the "paramstyle" accepted by the DBAPI
997 in use, which may be any of "qmark", "named", "pyformat", "format",
998 "numeric". See `pep-249 <http://www.python.org/dev/peps/pep-0249/>`_
999 for details on paramstyle.
1001 To execute a textual SQL statement which uses bound parameters in a
1002 DBAPI-agnostic way, use the :func:`_expression.text` construct.
1004 """
1005 if isinstance(object_, util.string_types[0]):
1006 return self._execute_text(object_, multiparams, params)
1007 try:
1008 meth = object_._execute_on_connection
1009 except AttributeError as err:
1010 util.raise_(
1011 exc.ObjectNotExecutableError(object_), replace_context=err
1012 )
1013 else:
1014 return meth(self, multiparams, params)
1016 def _execute_function(self, func, multiparams, params):
1017 """Execute a sql.FunctionElement object."""
1019 return self._execute_clauseelement(func.select(), multiparams, params)
1021 def _execute_default(self, default, multiparams, params):
1022 """Execute a schema.ColumnDefault object."""
1024 if self._has_events or self.engine._has_events:
1025 for fn in self.dispatch.before_execute:
1026 default, multiparams, params = fn(
1027 self, default, multiparams, params
1028 )
1030 try:
1031 try:
1032 conn = self.__connection
1033 except AttributeError:
1034 # escape "except AttributeError" before revalidating
1035 # to prevent misleading stacktraces in Py3K
1036 conn = None
1037 if conn is None:
1038 conn = self._revalidate_connection()
1040 dialect = self.dialect
1041 ctx = dialect.execution_ctx_cls._init_default(dialect, self, conn)
1042 except BaseException as e:
1043 self._handle_dbapi_exception(e, None, None, None, None)
1045 ret = ctx._exec_default(None, default, None)
1046 if self.should_close_with_result:
1047 self.close()
1049 if self._has_events or self.engine._has_events:
1050 self.dispatch.after_execute(
1051 self, default, multiparams, params, ret
1052 )
1054 return ret
1056 def _execute_ddl(self, ddl, multiparams, params):
1057 """Execute a schema.DDL object."""
1059 if self._has_events or self.engine._has_events:
1060 for fn in self.dispatch.before_execute:
1061 ddl, multiparams, params = fn(self, ddl, multiparams, params)
1063 dialect = self.dialect
1065 compiled = ddl.compile(
1066 dialect=dialect,
1067 schema_translate_map=self.schema_for_object
1068 if not self.schema_for_object.is_default
1069 else None,
1070 )
1071 ret = self._execute_context(
1072 dialect,
1073 dialect.execution_ctx_cls._init_ddl,
1074 compiled,
1075 None,
1076 compiled,
1077 )
1078 if self._has_events or self.engine._has_events:
1079 self.dispatch.after_execute(self, ddl, multiparams, params, ret)
1080 return ret
1082 def _execute_clauseelement(self, elem, multiparams, params):
1083 """Execute a sql.ClauseElement object."""
1085 if self._has_events or self.engine._has_events:
1086 for fn in self.dispatch.before_execute:
1087 elem, multiparams, params = fn(self, elem, multiparams, params)
1089 distilled_params = _distill_params(multiparams, params)
1090 if distilled_params:
1091 # ensure we don't retain a link to the view object for keys()
1092 # which links to the values, which we don't want to cache
1093 keys = list(distilled_params[0].keys())
1094 else:
1095 keys = []
1097 dialect = self.dialect
1098 if "compiled_cache" in self._execution_options:
1099 key = (
1100 dialect,
1101 elem,
1102 tuple(sorted(keys)),
1103 self.schema_for_object.hash_key,
1104 len(distilled_params) > 1,
1105 )
1106 compiled_sql = self._execution_options["compiled_cache"].get(key)
1107 if compiled_sql is None:
1108 compiled_sql = elem.compile(
1109 dialect=dialect,
1110 column_keys=keys,
1111 inline=len(distilled_params) > 1,
1112 schema_translate_map=self.schema_for_object
1113 if not self.schema_for_object.is_default
1114 else None,
1115 )
1116 self._execution_options["compiled_cache"][key] = compiled_sql
1117 else:
1118 compiled_sql = elem.compile(
1119 dialect=dialect,
1120 column_keys=keys,
1121 inline=len(distilled_params) > 1,
1122 schema_translate_map=self.schema_for_object
1123 if not self.schema_for_object.is_default
1124 else None,
1125 )
1127 ret = self._execute_context(
1128 dialect,
1129 dialect.execution_ctx_cls._init_compiled,
1130 compiled_sql,
1131 distilled_params,
1132 compiled_sql,
1133 distilled_params,
1134 )
1135 if self._has_events or self.engine._has_events:
1136 self.dispatch.after_execute(self, elem, multiparams, params, ret)
1137 return ret
1139 def _execute_compiled(self, compiled, multiparams, params):
1140 """Execute a sql.Compiled object."""
1142 if self._has_events or self.engine._has_events:
1143 for fn in self.dispatch.before_execute:
1144 compiled, multiparams, params = fn(
1145 self, compiled, multiparams, params
1146 )
1148 dialect = self.dialect
1149 parameters = _distill_params(multiparams, params)
1150 ret = self._execute_context(
1151 dialect,
1152 dialect.execution_ctx_cls._init_compiled,
1153 compiled,
1154 parameters,
1155 compiled,
1156 parameters,
1157 )
1158 if self._has_events or self.engine._has_events:
1159 self.dispatch.after_execute(
1160 self, compiled, multiparams, params, ret
1161 )
1162 return ret
1164 def _execute_text(self, statement, multiparams, params):
1165 """Execute a string SQL statement."""
1167 if self._has_events or self.engine._has_events:
1168 for fn in self.dispatch.before_execute:
1169 statement, multiparams, params = fn(
1170 self, statement, multiparams, params
1171 )
1173 dialect = self.dialect
1174 parameters = _distill_params(multiparams, params)
1175 ret = self._execute_context(
1176 dialect,
1177 dialect.execution_ctx_cls._init_statement,
1178 statement,
1179 parameters,
1180 statement,
1181 parameters,
1182 )
1183 if self._has_events or self.engine._has_events:
1184 self.dispatch.after_execute(
1185 self, statement, multiparams, params, ret
1186 )
1187 return ret
1189 def _execute_context(
1190 self, dialect, constructor, statement, parameters, *args
1191 ):
1192 """Create an :class:`.ExecutionContext` and execute, returning
1193 a :class:`_engine.ResultProxy`."""
1195 try:
1196 try:
1197 conn = self.__connection
1198 except AttributeError:
1199 # escape "except AttributeError" before revalidating
1200 # to prevent misleading stacktraces in Py3K
1201 conn = None
1202 if conn is None:
1203 conn = self._revalidate_connection()
1205 context = constructor(dialect, self, conn, *args)
1206 except BaseException as e:
1207 self._handle_dbapi_exception(
1208 e, util.text_type(statement), parameters, None, None
1209 )
1211 if context.compiled:
1212 context.pre_exec()
1214 cursor, statement, parameters = (
1215 context.cursor,
1216 context.statement,
1217 context.parameters,
1218 )
1220 if not context.executemany:
1221 parameters = parameters[0]
1223 if self._has_events or self.engine._has_events:
1224 for fn in self.dispatch.before_cursor_execute:
1225 statement, parameters = fn(
1226 self,
1227 cursor,
1228 statement,
1229 parameters,
1230 context,
1231 context.executemany,
1232 )
1234 if self._echo:
1235 self.engine.logger.info(statement)
1236 if not self.engine.hide_parameters:
1237 self.engine.logger.info(
1238 "%r",
1239 sql_util._repr_params(
1240 parameters, batches=10, ismulti=context.executemany
1241 ),
1242 )
1243 else:
1244 self.engine.logger.info(
1245 "[SQL parameters hidden due to hide_parameters=True]"
1246 )
1248 evt_handled = False
1249 try:
1250 if context.executemany:
1251 if self.dialect._has_events:
1252 for fn in self.dialect.dispatch.do_executemany:
1253 if fn(cursor, statement, parameters, context):
1254 evt_handled = True
1255 break
1256 if not evt_handled:
1257 self.dialect.do_executemany(
1258 cursor, statement, parameters, context
1259 )
1260 elif not parameters and context.no_parameters:
1261 if self.dialect._has_events:
1262 for fn in self.dialect.dispatch.do_execute_no_params:
1263 if fn(cursor, statement, context):
1264 evt_handled = True
1265 break
1266 if not evt_handled:
1267 self.dialect.do_execute_no_params(
1268 cursor, statement, context
1269 )
1270 else:
1271 if self.dialect._has_events:
1272 for fn in self.dialect.dispatch.do_execute:
1273 if fn(cursor, statement, parameters, context):
1274 evt_handled = True
1275 break
1276 if not evt_handled:
1277 self.dialect.do_execute(
1278 cursor, statement, parameters, context
1279 )
1281 if self._has_events or self.engine._has_events:
1282 self.dispatch.after_cursor_execute(
1283 self,
1284 cursor,
1285 statement,
1286 parameters,
1287 context,
1288 context.executemany,
1289 )
1291 if context.compiled:
1292 context.post_exec()
1294 if context.is_crud or context.is_text:
1295 result = context._setup_crud_result_proxy()
1296 else:
1297 result = context.get_result_proxy()
1298 if result._metadata is None:
1299 result._soft_close()
1301 if context.should_autocommit and self._root.__transaction is None:
1302 self._root._commit_impl(autocommit=True)
1304 # for "connectionless" execution, we have to close this
1305 # Connection after the statement is complete.
1306 if self.should_close_with_result:
1307 # ResultProxy already exhausted rows / has no rows.
1308 # close us now
1309 if result._soft_closed:
1310 self.close()
1311 else:
1312 # ResultProxy will close this Connection when no more
1313 # rows to fetch.
1314 result._autoclose_connection = True
1316 except BaseException as e:
1317 self._handle_dbapi_exception(
1318 e, statement, parameters, cursor, context
1319 )
1321 return result
1323 def _cursor_execute(self, cursor, statement, parameters, context=None):
1324 """Execute a statement + params on the given cursor.
1326 Adds appropriate logging and exception handling.
1328 This method is used by DefaultDialect for special-case
1329 executions, such as for sequences and column defaults.
1330 The path of statement execution in the majority of cases
1331 terminates at _execute_context().
1333 """
1334 if self._has_events or self.engine._has_events:
1335 for fn in self.dispatch.before_cursor_execute:
1336 statement, parameters = fn(
1337 self, cursor, statement, parameters, context, False
1338 )
1340 if self._echo:
1341 self.engine.logger.info(statement)
1342 self.engine.logger.info("%r", parameters)
1343 try:
1344 for fn in (
1345 ()
1346 if not self.dialect._has_events
1347 else self.dialect.dispatch.do_execute
1348 ):
1349 if fn(cursor, statement, parameters, context):
1350 break
1351 else:
1352 self.dialect.do_execute(cursor, statement, parameters, context)
1353 except BaseException as e:
1354 self._handle_dbapi_exception(
1355 e, statement, parameters, cursor, context
1356 )
1358 if self._has_events or self.engine._has_events:
1359 self.dispatch.after_cursor_execute(
1360 self, cursor, statement, parameters, context, False
1361 )
1363 def _safe_close_cursor(self, cursor):
1364 """Close the given cursor, catching exceptions
1365 and turning into log warnings.
1367 """
1368 try:
1369 cursor.close()
1370 except Exception:
1371 # log the error through the connection pool's logger.
1372 self.engine.pool.logger.error(
1373 "Error closing cursor", exc_info=True
1374 )
1376 _reentrant_error = False
1377 _is_disconnect = False
1379 def _handle_dbapi_exception(
1380 self, e, statement, parameters, cursor, context
1381 ):
1382 exc_info = sys.exc_info()
1384 if context and context.exception is None:
1385 context.exception = e
1387 is_exit_exception = not isinstance(e, Exception)
1389 if not self._is_disconnect:
1390 self._is_disconnect = (
1391 isinstance(e, self.dialect.dbapi.Error)
1392 and not self.closed
1393 and self.dialect.is_disconnect(
1394 e,
1395 self.__connection if not self.invalidated else None,
1396 cursor,
1397 )
1398 ) or (is_exit_exception and not self.closed)
1400 if context:
1401 context.is_disconnect = self._is_disconnect
1403 invalidate_pool_on_disconnect = not is_exit_exception
1405 if self._reentrant_error:
1406 util.raise_(
1407 exc.DBAPIError.instance(
1408 statement,
1409 parameters,
1410 e,
1411 self.dialect.dbapi.Error,
1412 hide_parameters=self.engine.hide_parameters,
1413 dialect=self.dialect,
1414 ismulti=context.executemany
1415 if context is not None
1416 else None,
1417 ),
1418 with_traceback=exc_info[2],
1419 from_=e,
1420 )
1421 self._reentrant_error = True
1422 try:
1423 # non-DBAPI error - if we already got a context,
1424 # or there's no string statement, don't wrap it
1425 should_wrap = isinstance(e, self.dialect.dbapi.Error) or (
1426 statement is not None
1427 and context is None
1428 and not is_exit_exception
1429 )
1431 if should_wrap:
1432 sqlalchemy_exception = exc.DBAPIError.instance(
1433 statement,
1434 parameters,
1435 e,
1436 self.dialect.dbapi.Error,
1437 hide_parameters=self.engine.hide_parameters,
1438 connection_invalidated=self._is_disconnect,
1439 dialect=self.dialect,
1440 ismulti=context.executemany
1441 if context is not None
1442 else None,
1443 )
1444 else:
1445 sqlalchemy_exception = None
1447 newraise = None
1449 if (
1450 self._has_events or self.engine._has_events
1451 ) and not self._execution_options.get(
1452 "skip_user_error_events", False
1453 ):
1454 # legacy dbapi_error event
1455 if should_wrap and context:
1456 self.dispatch.dbapi_error(
1457 self, cursor, statement, parameters, context, e
1458 )
1460 # new handle_error event
1461 ctx = ExceptionContextImpl(
1462 e,
1463 sqlalchemy_exception,
1464 self.engine,
1465 self,
1466 cursor,
1467 statement,
1468 parameters,
1469 context,
1470 self._is_disconnect,
1471 invalidate_pool_on_disconnect,
1472 )
1474 for fn in self.dispatch.handle_error:
1475 try:
1476 # handler returns an exception;
1477 # call next handler in a chain
1478 per_fn = fn(ctx)
1479 if per_fn is not None:
1480 ctx.chained_exception = newraise = per_fn
1481 except Exception as _raised:
1482 # handler raises an exception - stop processing
1483 newraise = _raised
1484 break
1486 if self._is_disconnect != ctx.is_disconnect:
1487 self._is_disconnect = ctx.is_disconnect
1488 if sqlalchemy_exception:
1489 sqlalchemy_exception.connection_invalidated = (
1490 ctx.is_disconnect
1491 )
1493 # set up potentially user-defined value for
1494 # invalidate pool.
1495 invalidate_pool_on_disconnect = (
1496 ctx.invalidate_pool_on_disconnect
1497 )
1499 if should_wrap and context:
1500 context.handle_dbapi_exception(e)
1502 if not self._is_disconnect:
1503 if cursor:
1504 self._safe_close_cursor(cursor)
1505 with util.safe_reraise(warn_only=True):
1506 self._autorollback()
1508 if newraise:
1509 util.raise_(newraise, with_traceback=exc_info[2], from_=e)
1510 elif should_wrap:
1511 util.raise_(
1512 sqlalchemy_exception, with_traceback=exc_info[2], from_=e
1513 )
1514 else:
1515 util.raise_(exc_info[1], with_traceback=exc_info[2])
1517 finally:
1518 del self._reentrant_error
1519 if self._is_disconnect:
1520 del self._is_disconnect
1521 if not self.invalidated:
1522 dbapi_conn_wrapper = self.__connection
1523 if invalidate_pool_on_disconnect:
1524 self.engine.pool._invalidate(dbapi_conn_wrapper, e)
1525 self.invalidate(e)
1526 if self.should_close_with_result:
1527 self.close()
1529 @classmethod
1530 def _handle_dbapi_exception_noconnection(cls, e, dialect, engine):
1531 exc_info = sys.exc_info()
1533 is_disconnect = dialect.is_disconnect(e, None, None)
1535 should_wrap = isinstance(e, dialect.dbapi.Error)
1537 if should_wrap:
1538 sqlalchemy_exception = exc.DBAPIError.instance(
1539 None,
1540 None,
1541 e,
1542 dialect.dbapi.Error,
1543 hide_parameters=engine.hide_parameters,
1544 connection_invalidated=is_disconnect,
1545 )
1546 else:
1547 sqlalchemy_exception = None
1549 newraise = None
1551 if engine._has_events:
1552 ctx = ExceptionContextImpl(
1553 e,
1554 sqlalchemy_exception,
1555 engine,
1556 None,
1557 None,
1558 None,
1559 None,
1560 None,
1561 is_disconnect,
1562 True,
1563 )
1564 for fn in engine.dispatch.handle_error:
1565 try:
1566 # handler returns an exception;
1567 # call next handler in a chain
1568 per_fn = fn(ctx)
1569 if per_fn is not None:
1570 ctx.chained_exception = newraise = per_fn
1571 except Exception as _raised:
1572 # handler raises an exception - stop processing
1573 newraise = _raised
1574 break
1576 if sqlalchemy_exception and is_disconnect != ctx.is_disconnect:
1577 sqlalchemy_exception.connection_invalidated = (
1578 is_disconnect
1579 ) = ctx.is_disconnect
1581 if newraise:
1582 util.raise_(newraise, with_traceback=exc_info[2], from_=e)
1583 elif should_wrap:
1584 util.raise_(
1585 sqlalchemy_exception, with_traceback=exc_info[2], from_=e
1586 )
1587 else:
1588 util.raise_(exc_info[1], with_traceback=exc_info[2])
1590 def transaction(self, callable_, *args, **kwargs):
1591 r"""Execute the given function within a transaction boundary.
1593 The function is passed this :class:`_engine.Connection`
1594 as the first argument, followed by the given \*args and \**kwargs,
1595 e.g.::
1597 def do_something(conn, x, y):
1598 conn.execute("some statement", {'x':x, 'y':y})
1600 conn.transaction(do_something, 5, 10)
1602 The operations inside the function are all invoked within the
1603 context of a single :class:`.Transaction`.
1604 Upon success, the transaction is committed. If an
1605 exception is raised, the transaction is rolled back
1606 before propagating the exception.
1608 .. note::
1610 The :meth:`.transaction` method is superseded by
1611 the usage of the Python ``with:`` statement, which can
1612 be used with :meth:`_engine.Connection.begin`::
1614 with conn.begin():
1615 conn.execute("some statement", {'x':5, 'y':10})
1617 As well as with :meth:`_engine.Engine.begin`::
1619 with engine.begin() as conn:
1620 conn.execute("some statement", {'x':5, 'y':10})
1622 .. seealso::
1624 :meth:`_engine.Engine.begin` - engine-level transactional
1625 context
1627 :meth:`_engine.Engine.transaction` - engine-level version of
1628 :meth:`_engine.Connection.transaction`
1630 """
1632 trans = self.begin()
1633 try:
1634 ret = self.run_callable(callable_, *args, **kwargs)
1635 trans.commit()
1636 return ret
1637 except:
1638 with util.safe_reraise():
1639 trans.rollback()
1641 def run_callable(self, callable_, *args, **kwargs):
1642 r"""Given a callable object or function, execute it, passing
1643 a :class:`_engine.Connection` as the first argument.
1645 The given \*args and \**kwargs are passed subsequent
1646 to the :class:`_engine.Connection` argument.
1648 This function, along with :meth:`_engine.Engine.run_callable`,
1649 allows a function to be run with a :class:`_engine.Connection`
1650 or :class:`_engine.Engine` object without the need to know
1651 which one is being dealt with.
1653 """
1654 return callable_(self, *args, **kwargs)
1656 def _run_visitor(self, visitorcallable, element, **kwargs):
1657 visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
1660class ExceptionContextImpl(ExceptionContext):
1661 """Implement the :class:`.ExceptionContext` interface."""
1663 def __init__(
1664 self,
1665 exception,
1666 sqlalchemy_exception,
1667 engine,
1668 connection,
1669 cursor,
1670 statement,
1671 parameters,
1672 context,
1673 is_disconnect,
1674 invalidate_pool_on_disconnect,
1675 ):
1676 self.engine = engine
1677 self.connection = connection
1678 self.sqlalchemy_exception = sqlalchemy_exception
1679 self.original_exception = exception
1680 self.execution_context = context
1681 self.statement = statement
1682 self.parameters = parameters
1683 self.is_disconnect = is_disconnect
1684 self.invalidate_pool_on_disconnect = invalidate_pool_on_disconnect
1687class Transaction(object):
1688 """Represent a database transaction in progress.
1690 The :class:`.Transaction` object is procured by
1691 calling the :meth:`_engine.Connection.begin` method of
1692 :class:`_engine.Connection`::
1694 from sqlalchemy import create_engine
1695 engine = create_engine("postgresql://scott:tiger@localhost/test")
1696 connection = engine.connect()
1697 trans = connection.begin()
1698 connection.execute("insert into x (a, b) values (1, 2)")
1699 trans.commit()
1701 The object provides :meth:`.rollback` and :meth:`.commit`
1702 methods in order to control transaction boundaries. It
1703 also implements a context manager interface so that
1704 the Python ``with`` statement can be used with the
1705 :meth:`_engine.Connection.begin` method::
1707 with connection.begin():
1708 connection.execute("insert into x (a, b) values (1, 2)")
1710 The Transaction object is **not** threadsafe.
1712 .. seealso::
1714 :meth:`_engine.Connection.begin`
1716 :meth:`_engine.Connection.begin_twophase`
1718 :meth:`_engine.Connection.begin_nested`
1720 .. index::
1721 single: thread safety; Transaction
1722 """
1724 def __init__(self, connection, parent):
1725 self.connection = connection
1726 self._actual_parent = parent
1727 self.is_active = True
1729 @property
1730 def _parent(self):
1731 return self._actual_parent or self
1733 def close(self):
1734 """Close this :class:`.Transaction`.
1736 If this transaction is the base transaction in a begin/commit
1737 nesting, the transaction will rollback(). Otherwise, the
1738 method returns.
1740 This is used to cancel a Transaction without affecting the scope of
1741 an enclosing transaction.
1743 """
1745 if self._parent.is_active and self._parent is self:
1746 self.rollback()
1747 self.connection._discard_transaction(self)
1749 def rollback(self):
1750 """Roll back this :class:`.Transaction`.
1752 """
1753 if self._parent.is_active:
1754 self._do_rollback()
1755 self.is_active = False
1757 def _do_rollback(self):
1758 self._parent.rollback()
1760 def commit(self):
1761 """Commit this :class:`.Transaction`."""
1763 if not self._parent.is_active:
1764 raise exc.InvalidRequestError("This transaction is inactive")
1765 self._do_commit()
1766 self.is_active = False
1768 def _do_commit(self):
1769 pass
1771 def __enter__(self):
1772 return self
1774 def __exit__(self, type_, value, traceback):
1775 if type_ is None and self.is_active:
1776 try:
1777 self.commit()
1778 except:
1779 with util.safe_reraise():
1780 self.rollback()
1781 else:
1782 self.rollback()
1785class RootTransaction(Transaction):
1786 def __init__(self, connection):
1787 super(RootTransaction, self).__init__(connection, None)
1788 self.connection._begin_impl(self)
1790 def _do_rollback(self):
1791 if self.is_active:
1792 self.connection._rollback_impl()
1794 def _do_commit(self):
1795 if self.is_active:
1796 self.connection._commit_impl()
1799class NestedTransaction(Transaction):
1800 """Represent a 'nested', or SAVEPOINT transaction.
1802 A new :class:`.NestedTransaction` object may be procured
1803 using the :meth:`_engine.Connection.begin_nested` method.
1805 The interface is the same as that of :class:`.Transaction`.
1807 """
1809 def __init__(self, connection, parent):
1810 super(NestedTransaction, self).__init__(connection, parent)
1811 self._savepoint = self.connection._savepoint_impl()
1813 def _do_rollback(self):
1814 if self.is_active:
1815 self.connection._rollback_to_savepoint_impl(
1816 self._savepoint, self._parent
1817 )
1819 def _do_commit(self):
1820 if self.is_active:
1821 self.connection._release_savepoint_impl(
1822 self._savepoint, self._parent
1823 )
1826class TwoPhaseTransaction(Transaction):
1827 """Represent a two-phase transaction.
1829 A new :class:`.TwoPhaseTransaction` object may be procured
1830 using the :meth:`_engine.Connection.begin_twophase` method.
1832 The interface is the same as that of :class:`.Transaction`
1833 with the addition of the :meth:`prepare` method.
1835 """
1837 def __init__(self, connection, xid):
1838 super(TwoPhaseTransaction, self).__init__(connection, None)
1839 self._is_prepared = False
1840 self.xid = xid
1841 self.connection._begin_twophase_impl(self)
1843 def prepare(self):
1844 """Prepare this :class:`.TwoPhaseTransaction`.
1846 After a PREPARE, the transaction can be committed.
1848 """
1849 if not self._parent.is_active:
1850 raise exc.InvalidRequestError("This transaction is inactive")
1851 self.connection._prepare_twophase_impl(self.xid)
1852 self._is_prepared = True
1854 def _do_rollback(self):
1855 self.connection._rollback_twophase_impl(self.xid, self._is_prepared)
1857 def _do_commit(self):
1858 self.connection._commit_twophase_impl(self.xid, self._is_prepared)
1861class Engine(Connectable, log.Identified):
1862 """
1863 Connects a :class:`~sqlalchemy.pool.Pool` and
1864 :class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a
1865 source of database connectivity and behavior.
1867 An :class:`_engine.Engine` object is instantiated publicly using the
1868 :func:`~sqlalchemy.create_engine` function.
1870 .. seealso::
1872 :doc:`/core/engines`
1874 :ref:`connections_toplevel`
1876 """
1878 _execution_options = util.immutabledict()
1879 _has_events = False
1880 _connection_cls = Connection
1882 schema_for_object = schema._schema_getter(None)
1883 """Return the ".schema" attribute for an object.
1885 Used for :class:`_schema.Table`, :class:`.Sequence` and similar objects,
1886 and takes into account
1887 the :paramref:`.Connection.execution_options.schema_translate_map`
1888 parameter.
1890 .. versionadded:: 1.1
1892 .. seealso::
1894 :ref:`schema_translating`
1896 """
1898 def __init__(
1899 self,
1900 pool,
1901 dialect,
1902 url,
1903 logging_name=None,
1904 echo=None,
1905 proxy=None,
1906 execution_options=None,
1907 hide_parameters=False,
1908 ):
1909 self.pool = pool
1910 self.url = url
1911 self.dialect = dialect
1912 if logging_name:
1913 self.logging_name = logging_name
1914 self.echo = echo
1915 self.hide_parameters = hide_parameters
1916 log.instance_logger(self, echoflag=echo)
1917 if proxy:
1918 interfaces.ConnectionProxy._adapt_listener(self, proxy)
1919 if execution_options:
1920 self.update_execution_options(**execution_options)
1922 @property
1923 def engine(self):
1924 return self
1926 def update_execution_options(self, **opt):
1927 r"""Update the default execution_options dictionary
1928 of this :class:`_engine.Engine`.
1930 The given keys/values in \**opt are added to the
1931 default execution options that will be used for
1932 all connections. The initial contents of this dictionary
1933 can be sent via the ``execution_options`` parameter
1934 to :func:`_sa.create_engine`.
1936 .. seealso::
1938 :meth:`_engine.Connection.execution_options`
1940 :meth:`_engine.Engine.execution_options`
1942 """
1943 self._execution_options = self._execution_options.union(opt)
1944 self.dispatch.set_engine_execution_options(self, opt)
1945 self.dialect.set_engine_execution_options(self, opt)
1947 def execution_options(self, **opt):
1948 """Return a new :class:`_engine.Engine` that will provide
1949 :class:`_engine.Connection` objects with the given execution options.
1951 The returned :class:`_engine.Engine` remains related to the original
1952 :class:`_engine.Engine` in that it shares the same connection pool and
1953 other state:
1955 * The :class:`_pool.Pool` used by the new :class:`_engine.Engine`
1956 is the
1957 same instance. The :meth:`_engine.Engine.dispose`
1958 method will replace
1959 the connection pool instance for the parent engine as well
1960 as this one.
1961 * Event listeners are "cascaded" - meaning, the new
1962 :class:`_engine.Engine`
1963 inherits the events of the parent, and new events can be associated
1964 with the new :class:`_engine.Engine` individually.
1965 * The logging configuration and logging_name is copied from the parent
1966 :class:`_engine.Engine`.
1968 The intent of the :meth:`_engine.Engine.execution_options` method is
1969 to implement "sharding" schemes where multiple :class:`_engine.Engine`
1970 objects refer to the same connection pool, but are differentiated
1971 by options that would be consumed by a custom event::
1973 primary_engine = create_engine("mysql://")
1974 shard1 = primary_engine.execution_options(shard_id="shard1")
1975 shard2 = primary_engine.execution_options(shard_id="shard2")
1977 Above, the ``shard1`` engine serves as a factory for
1978 :class:`_engine.Connection`
1979 objects that will contain the execution option
1980 ``shard_id=shard1``, and ``shard2`` will produce
1981 :class:`_engine.Connection`
1982 objects that contain the execution option ``shard_id=shard2``.
1984 An event handler can consume the above execution option to perform
1985 a schema switch or other operation, given a connection. Below
1986 we emit a MySQL ``use`` statement to switch databases, at the same
1987 time keeping track of which database we've established using the
1988 :attr:`_engine.Connection.info` dictionary,
1989 which gives us a persistent
1990 storage space that follows the DBAPI connection::
1992 from sqlalchemy import event
1993 from sqlalchemy.engine import Engine
1995 shards = {"default": "base", shard_1: "db1", "shard_2": "db2"}
1997 @event.listens_for(Engine, "before_cursor_execute")
1998 def _switch_shard(conn, cursor, stmt,
1999 params, context, executemany):
2000 shard_id = conn._execution_options.get('shard_id', "default")
2001 current_shard = conn.info.get("current_shard", None)
2003 if current_shard != shard_id:
2004 cursor.execute("use %s" % shards[shard_id])
2005 conn.info["current_shard"] = shard_id
2007 .. seealso::
2009 :meth:`_engine.Connection.execution_options`
2010 - update execution options
2011 on a :class:`_engine.Connection` object.
2013 :meth:`_engine.Engine.update_execution_options`
2014 - update the execution
2015 options for a given :class:`_engine.Engine` in place.
2017 :meth:`_engine.Engine.get_execution_options`
2020 """
2021 return OptionEngine(self, opt)
2023 def get_execution_options(self):
2024 """ Get the non-SQL options which will take effect during execution.
2026 .. versionadded: 1.3
2028 .. seealso::
2030 :meth:`_engine.Engine.execution_options`
2031 """
2032 return self._execution_options
2034 @property
2035 def name(self):
2036 """String name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
2037 in use by this :class:`Engine`."""
2039 return self.dialect.name
2041 @property
2042 def driver(self):
2043 """Driver name of the :class:`~sqlalchemy.engine.interfaces.Dialect`
2044 in use by this :class:`Engine`."""
2046 return self.dialect.driver
2048 echo = log.echo_property()
2050 def __repr__(self):
2051 return "Engine(%r)" % self.url
2053 def dispose(self):
2054 """Dispose of the connection pool used by this :class:`_engine.Engine`
2055 .
2057 This has the effect of fully closing all **currently checked in**
2058 database connections. Connections that are still checked out
2059 will **not** be closed, however they will no longer be associated
2060 with this :class:`_engine.Engine`,
2061 so when they are closed individually,
2062 eventually the :class:`_pool.Pool` which they are associated with will
2063 be garbage collected and they will be closed out fully, if
2064 not already closed on checkin.
2066 A new connection pool is created immediately after the old one has
2067 been disposed. This new pool, like all SQLAlchemy connection pools,
2068 does not make any actual connections to the database until one is
2069 first requested, so as long as the :class:`_engine.Engine`
2070 isn't used again,
2071 no new connections will be made.
2073 .. seealso::
2075 :ref:`engine_disposal`
2077 """
2078 self.pool.dispose()
2079 self.pool = self.pool.recreate()
2080 self.dispatch.engine_disposed(self)
2082 def _execute_default(self, default):
2083 with self._contextual_connect() as conn:
2084 return conn._execute_default(default, (), {})
2086 @contextlib.contextmanager
2087 def _optional_conn_ctx_manager(self, connection=None):
2088 if connection is None:
2089 with self._contextual_connect() as conn:
2090 yield conn
2091 else:
2092 yield connection
2094 def _run_visitor(
2095 self, visitorcallable, element, connection=None, **kwargs
2096 ):
2097 with self._optional_conn_ctx_manager(connection) as conn:
2098 conn._run_visitor(visitorcallable, element, **kwargs)
2100 class _trans_ctx(object):
2101 def __init__(self, conn, transaction, close_with_result):
2102 self.conn = conn
2103 self.transaction = transaction
2104 self.close_with_result = close_with_result
2106 def __enter__(self):
2107 return self.conn
2109 def __exit__(self, type_, value, traceback):
2110 if type_ is not None:
2111 self.transaction.rollback()
2112 else:
2113 self.transaction.commit()
2114 if not self.close_with_result:
2115 self.conn.close()
2117 def begin(self, close_with_result=False):
2118 """Return a context manager delivering a :class:`_engine.Connection`
2119 with a :class:`.Transaction` established.
2121 E.g.::
2123 with engine.begin() as conn:
2124 conn.execute("insert into table (x, y, z) values (1, 2, 3)")
2125 conn.execute("my_special_procedure(5)")
2127 Upon successful operation, the :class:`.Transaction`
2128 is committed. If an error is raised, the :class:`.Transaction`
2129 is rolled back.
2131 The ``close_with_result`` flag is normally ``False``, and indicates
2132 that the :class:`_engine.Connection` will be closed when the operation
2133 is complete. When set to ``True``, it indicates the
2134 :class:`_engine.Connection` is in "single use" mode, where the
2135 :class:`_engine.ResultProxy` returned by the first call to
2136 :meth:`_engine.Connection.execute` will close the
2137 :class:`_engine.Connection` when
2138 that :class:`_engine.ResultProxy` has exhausted all result rows.
2140 .. seealso::
2142 :meth:`_engine.Engine.connect` - procure a
2143 :class:`_engine.Connection` from
2144 an :class:`_engine.Engine`.
2146 :meth:`_engine.Connection.begin` - start a :class:`.Transaction`
2147 for a particular :class:`_engine.Connection`.
2149 """
2150 conn = self._contextual_connect(close_with_result=close_with_result)
2151 try:
2152 trans = conn.begin()
2153 except:
2154 with util.safe_reraise():
2155 conn.close()
2156 return Engine._trans_ctx(conn, trans, close_with_result)
2158 def transaction(self, callable_, *args, **kwargs):
2159 r"""Execute the given function within a transaction boundary.
2161 The function is passed a :class:`_engine.Connection` newly procured
2162 from :meth:`_engine.Engine.contextual_connect` as the first argument,
2163 followed by the given \*args and \**kwargs.
2165 e.g.::
2167 def do_something(conn, x, y):
2168 conn.execute("some statement", {'x':x, 'y':y})
2170 engine.transaction(do_something, 5, 10)
2172 The operations inside the function are all invoked within the
2173 context of a single :class:`.Transaction`.
2174 Upon success, the transaction is committed. If an
2175 exception is raised, the transaction is rolled back
2176 before propagating the exception.
2178 .. note::
2180 The :meth:`.transaction` method is superseded by
2181 the usage of the Python ``with:`` statement, which can
2182 be used with :meth:`_engine.Engine.begin`::
2184 with engine.begin() as conn:
2185 conn.execute("some statement", {'x':5, 'y':10})
2187 .. seealso::
2189 :meth:`_engine.Engine.begin` - engine-level transactional
2190 context
2192 :meth:`_engine.Connection.transaction`
2193 - connection-level version of
2194 :meth:`_engine.Engine.transaction`
2196 """
2198 with self._contextual_connect() as conn:
2199 return conn.transaction(callable_, *args, **kwargs)
2201 def run_callable(self, callable_, *args, **kwargs):
2202 r"""Given a callable object or function, execute it, passing
2203 a :class:`_engine.Connection` as the first argument.
2205 The given \*args and \**kwargs are passed subsequent
2206 to the :class:`_engine.Connection` argument.
2208 This function, along with :meth:`_engine.Connection.run_callable`,
2209 allows a function to be run with a :class:`_engine.Connection`
2210 or :class:`_engine.Engine` object without the need to know
2211 which one is being dealt with.
2213 """
2214 with self._contextual_connect() as conn:
2215 return conn.run_callable(callable_, *args, **kwargs)
2217 def execute(self, statement, *multiparams, **params):
2218 """Executes the given construct and returns a
2219 :class:`_engine.ResultProxy`.
2221 The arguments are the same as those used by
2222 :meth:`_engine.Connection.execute`.
2224 Here, a :class:`_engine.Connection` is acquired using the
2225 :meth:`_engine.Engine.contextual_connect` method,
2226 and the statement executed
2227 with that connection. The returned :class:`_engine.ResultProxy`
2228 is flagged
2229 such that when the :class:`_engine.ResultProxy` is exhausted and its
2230 underlying cursor is closed, the :class:`_engine.Connection`
2231 created here
2232 will also be closed, which allows its associated DBAPI connection
2233 resource to be returned to the connection pool.
2235 """
2237 connection = self._contextual_connect(close_with_result=True)
2238 return connection.execute(statement, *multiparams, **params)
2240 def scalar(self, statement, *multiparams, **params):
2241 return self.execute(statement, *multiparams, **params).scalar()
2243 def _execute_clauseelement(self, elem, multiparams=None, params=None):
2244 connection = self._contextual_connect(close_with_result=True)
2245 return connection._execute_clauseelement(elem, multiparams, params)
2247 def _execute_compiled(self, compiled, multiparams, params):
2248 connection = self._contextual_connect(close_with_result=True)
2249 return connection._execute_compiled(compiled, multiparams, params)
2251 def connect(self, **kwargs):
2252 """Return a new :class:`_engine.Connection` object.
2254 The :class:`_engine.Connection` object is a facade that uses a DBAPI
2255 connection internally in order to communicate with the database. This
2256 connection is procured from the connection-holding :class:`_pool.Pool`
2257 referenced by this :class:`_engine.Engine`. When the
2258 :meth:`_engine.Connection.close` method of the
2259 :class:`_engine.Connection` object
2260 is called, the underlying DBAPI connection is then returned to the
2261 connection pool, where it may be used again in a subsequent call to
2262 :meth:`_engine.Engine.connect`.
2264 """
2266 return self._connection_cls(self, **kwargs)
2268 @util.deprecated(
2269 "1.3",
2270 "The :meth:`_engine.Engine.contextual_connect` method is deprecated. "
2271 "This "
2272 "method is an artifact of the threadlocal engine strategy which is "
2273 "also to be deprecated. For explicit connections from an "
2274 ":class:`_engine.Engine`, use the :meth:`_engine.Engine.connect` "
2275 "method.",
2276 )
2277 def contextual_connect(self, close_with_result=False, **kwargs):
2278 """Return a :class:`_engine.Connection`
2279 object which may be part of some
2280 ongoing context.
2282 By default, this method does the same thing as
2283 :meth:`_engine.Engine.connect`.
2284 Subclasses of :class:`_engine.Engine` may override this method
2285 to provide contextual behavior.
2287 :param close_with_result: When True, the first
2288 :class:`_engine.ResultProxy`
2289 created by the :class:`_engine.Connection` will call the
2290 :meth:`_engine.Connection.close`
2291 method of that connection as soon as any
2292 pending result rows are exhausted. This is used to supply the
2293 "connectionless execution" behavior provided by the
2294 :meth:`_engine.Engine.execute` method.
2296 """
2298 return self._contextual_connect(
2299 close_with_result=close_with_result, **kwargs
2300 )
2302 def _contextual_connect(self, close_with_result=False, **kwargs):
2303 return self._connection_cls(
2304 self,
2305 self._wrap_pool_connect(self.pool.connect, None),
2306 close_with_result=close_with_result,
2307 **kwargs
2308 )
2310 def table_names(self, schema=None, connection=None):
2311 """Return a list of all table names available in the database.
2313 :param schema: Optional, retrieve names from a non-default schema.
2315 :param connection: Optional, use a specified connection. Default is
2316 the ``contextual_connect`` for this ``Engine``.
2317 """
2319 with self._optional_conn_ctx_manager(connection) as conn:
2320 return self.dialect.get_table_names(conn, schema)
2322 def has_table(self, table_name, schema=None):
2323 """Return True if the given backend has a table of the given name.
2325 .. seealso::
2327 :ref:`metadata_reflection_inspector` - detailed schema inspection
2328 using the :class:`_reflection.Inspector` interface.
2330 :class:`.quoted_name` - used to pass quoting information along
2331 with a schema identifier.
2333 """
2334 return self.run_callable(self.dialect.has_table, table_name, schema)
2336 def _wrap_pool_connect(self, fn, connection):
2337 dialect = self.dialect
2338 try:
2339 return fn()
2340 except dialect.dbapi.Error as e:
2341 if connection is None:
2342 Connection._handle_dbapi_exception_noconnection(
2343 e, dialect, self
2344 )
2345 else:
2346 util.raise_(
2347 sys.exc_info()[1], with_traceback=sys.exc_info()[2]
2348 )
2350 def raw_connection(self, _connection=None):
2351 """Return a "raw" DBAPI connection from the connection pool.
2353 The returned object is a proxied version of the DBAPI
2354 connection object used by the underlying driver in use.
2355 The object will have all the same behavior as the real DBAPI
2356 connection, except that its ``close()`` method will result in the
2357 connection being returned to the pool, rather than being closed
2358 for real.
2360 This method provides direct DBAPI connection access for
2361 special situations when the API provided by
2362 :class:`_engine.Connection`
2363 is not needed. When a :class:`_engine.Connection` object is already
2364 present, the DBAPI connection is available using
2365 the :attr:`_engine.Connection.connection` accessor.
2367 .. seealso::
2369 :ref:`dbapi_connections`
2371 """
2372 return self._wrap_pool_connect(
2373 self.pool.unique_connection, _connection
2374 )
2377class OptionEngine(Engine):
2378 _sa_propagate_class_events = False
2380 def __init__(self, proxied, execution_options):
2381 self._proxied = proxied
2382 self.url = proxied.url
2383 self.dialect = proxied.dialect
2384 self.logging_name = proxied.logging_name
2385 self.echo = proxied.echo
2386 self.hide_parameters = proxied.hide_parameters
2387 log.instance_logger(self, echoflag=self.echo)
2389 # note: this will propagate events that are assigned to the parent
2390 # engine after this OptionEngine is created. Since we share
2391 # the events of the parent we also disallow class-level events
2392 # to apply to the OptionEngine class directly.
2393 #
2394 # the other way this can work would be to transfer existing
2395 # events only, using:
2396 # self.dispatch._update(proxied.dispatch)
2397 #
2398 # that might be more appropriate however it would be a behavioral
2399 # change for logic that assigns events to the parent engine and
2400 # would like it to take effect for the already-created sub-engine.
2401 self.dispatch = self.dispatch._join(proxied.dispatch)
2403 self._execution_options = proxied._execution_options
2404 self.update_execution_options(**execution_options)
2406 def _get_pool(self):
2407 return self._proxied.pool
2409 def _set_pool(self, pool):
2410 self._proxied.pool = pool
2412 pool = property(_get_pool, _set_pool)
2414 def _get_has_events(self):
2415 return self._proxied._has_events or self.__dict__.get(
2416 "_has_events", False
2417 )
2419 def _set_has_events(self, value):
2420 self.__dict__["_has_events"] = value
2422 _has_events = property(_get_has_events, _set_has_events)