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