update sqlalchemy

This commit is contained in:
j 2016-02-22 16:43:36 +05:30
commit 3b436646a2
362 changed files with 37720 additions and 11021 deletions

View file

@ -1,5 +1,5 @@
# engine/base.py
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2016 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@ -45,7 +45,7 @@ class Connection(Connectable):
"""
def __init__(self, engine, connection=None, close_with_result=False,
_branch=False, _execution_options=None,
_branch_from=None, _execution_options=None,
_dispatch=None,
_has_events=None):
"""Construct a new Connection.
@ -57,48 +57,80 @@ class Connection(Connectable):
"""
self.engine = engine
self.dialect = engine.dialect
self.__connection = connection or engine.raw_connection()
self.__transaction = None
self.should_close_with_result = close_with_result
self.__savepoint_seq = 0
self.__branch = _branch
self.__invalid = False
self.__can_reconnect = True
if _dispatch:
self.dispatch = _dispatch
elif _has_events is None:
# if _has_events is sent explicitly as False,
# then don't join the dispatch of the engine; we don't
# want to handle any of the engine's events in that case.
self.dispatch = self.dispatch._join(engine.dispatch)
self._has_events = _has_events or (
_has_events is None and engine._has_events)
self.__branch_from = _branch_from
self.__branch = _branch_from is not None
self._echo = self.engine._should_log_info()
if _execution_options:
self._execution_options =\
engine._execution_options.union(_execution_options)
if _branch_from:
self.__connection = connection
self._execution_options = _execution_options
self._echo = _branch_from._echo
self.should_close_with_result = False
self.dispatch = _dispatch
self._has_events = _branch_from._has_events
else:
self.__connection = connection \
if connection is not None else engine.raw_connection()
self.__transaction = None
self.__savepoint_seq = 0
self.should_close_with_result = close_with_result
self.__invalid = False
self.__can_reconnect = True
self._echo = self.engine._should_log_info()
if _has_events is None:
# if _has_events is sent explicitly as False,
# then don't join the dispatch of the engine; we don't
# want to handle any of the engine's events in that case.
self.dispatch = self.dispatch._join(engine.dispatch)
self._has_events = _has_events or (
_has_events is None and engine._has_events)
assert not _execution_options
self._execution_options = engine._execution_options
if self._has_events or self.engine._has_events:
self.dispatch.engine_connect(self, _branch)
self.dispatch.engine_connect(self, self.__branch)
def _branch(self):
"""Return a new Connection which references this Connection's
engine and connection; but does not have close_with_result enabled,
and also whose close() method does nothing.
This is used to execute "sub" statements within a single execution,
usually an INSERT statement.
The Core uses this very sparingly, only in the case of
custom SQL default functions that are to be INSERTed as the
primary key of a row where we need to get the value back, so we have
to invoke it distinctly - this is a very uncommon case.
Userland code accesses _branch() when the connect() or
contextual_connect() methods are called. The branched connection
acts as much as possible like the parent, except that it stays
connected when a close() event occurs.
"""
if self.__branch_from:
return self.__branch_from._branch()
else:
return self.engine._connection_cls(
self.engine,
self.__connection,
_branch_from=self,
_execution_options=self._execution_options,
_has_events=self._has_events,
_dispatch=self.dispatch)
@property
def _root(self):
"""return the 'root' connection.
Returns 'self' if this connection is not a branch, else
returns the root connection from which we ultimately branched.
"""
return self.engine._connection_cls(
self.engine,
self.__connection,
_branch=True,
_has_events=self._has_events,
_dispatch=self.dispatch)
if self.__branch_from:
return self.__branch_from
else:
return self
def _clone(self):
"""Create a shallow copy of this Connection.
@ -169,14 +201,19 @@ class Connection(Connectable):
used by the ORM internally supersedes a cache dictionary
specified here.
:param isolation_level: Available on: Connection.
:param isolation_level: Available on: :class:`.Connection`.
Set the transaction isolation level for
the lifespan of this connection. Valid values include
those string values accepted by the ``isolation_level``
parameter passed to :func:`.create_engine`, and are
database specific, including those for :ref:`sqlite_toplevel`,
:ref:`postgresql_toplevel` - see those dialect's documentation
for further info.
the lifespan of this :class:`.Connection` object (*not* the
underyling DBAPI connection, for which the level is reset
to its original setting upon termination of this
:class:`.Connection` object).
Valid values include
those string values accepted by the
:paramref:`.create_engine.isolation_level`
parameter passed to :func:`.create_engine`. These levels are
semi-database specific; see individual dialect documentation for
valid levels.
Note that this option necessarily affects the underlying
DBAPI connection for the lifespan of the originating
@ -185,6 +222,41 @@ class Connection(Connectable):
is returned to the connection pool, i.e.
the :meth:`.Connection.close` method is called.
.. warning:: The ``isolation_level`` execution option should
**not** be used when a transaction is already established, that
is, the :meth:`.Connection.begin` method or similar has been
called. A database cannot change the isolation level on a
transaction in progress, and different DBAPIs and/or
SQLAlchemy dialects may implicitly roll back or commit
the transaction, or not affect the connection at all.
.. versionchanged:: 0.9.9 A warning is emitted when the
``isolation_level`` execution option is used after a
transaction has been started with :meth:`.Connection.begin`
or similar.
.. note:: The ``isolation_level`` execution option is implicitly
reset if the :class:`.Connection` is invalidated, e.g. via
the :meth:`.Connection.invalidate` method, or if a
disconnection error occurs. The new connection produced after
the invalidation will not have the isolation level re-applied
to it automatically.
.. seealso::
:paramref:`.create_engine.isolation_level`
- set per :class:`.Engine` isolation level
:meth:`.Connection.get_isolation_level` - view current level
:ref:`SQLite Transaction Isolation <sqlite_isolation_level>`
:ref:`Postgresql Transaction Isolation <postgresql_isolation_level>`
:ref:`MySQL Transaction Isolation <mysql_isolation_level>`
:ref:`session_transaction_isolation` - for the ORM
:param no_parameters: When ``True``, if the final parameter
list or dictionary is totally empty, will invoke the
statement on the cursor as ``cursor.execute(statement)``,
@ -224,24 +296,101 @@ class Connection(Connectable):
def invalidated(self):
"""Return True if this connection was invalidated."""
return self.__invalid
return self._root.__invalid
@property
def connection(self):
"The underlying DB-API connection managed by this Connection."
"""The underlying DB-API connection managed by this Connection.
.. seealso::
:ref:`dbapi_connections`
"""
try:
return self.__connection
except AttributeError:
return self._revalidate_connection()
try:
return self._revalidate_connection()
except Exception as e:
self._handle_dbapi_exception(e, None, None, None, None)
def get_isolation_level(self):
"""Return the current isolation level assigned to this
:class:`.Connection`.
This will typically be the default isolation level as determined
by the dialect, unless if the
:paramref:`.Connection.execution_options.isolation_level`
feature has been used to alter the isolation level on a
per-:class:`.Connection` basis.
This attribute will typically perform a live SQL operation in order
to procure the current isolation level, so the value returned is the
actual level on the underlying DBAPI connection regardless of how
this state was set. Compare to the
:attr:`.Connection.default_isolation_level` accessor
which returns the dialect-level setting without performing a SQL
query.
.. versionadded:: 0.9.9
.. seealso::
:attr:`.Connection.default_isolation_level` - view default level
:paramref:`.create_engine.isolation_level`
- set per :class:`.Engine` isolation level
:paramref:`.Connection.execution_options.isolation_level`
- set per :class:`.Connection` isolation level
"""
try:
return self.dialect.get_isolation_level(self.connection)
except Exception as e:
self._handle_dbapi_exception(e, None, None, None, None)
@property
def default_isolation_level(self):
"""The default isolation level assigned to this :class:`.Connection`.
This is the isolation level setting that the :class:`.Connection`
has when first procured via the :meth:`.Engine.connect` method.
This level stays in place until the
:paramref:`.Connection.execution_options.isolation_level` is used
to change the setting on a per-:class:`.Connection` basis.
Unlike :meth:`.Connection.get_isolation_level`, this attribute is set
ahead of time from the first connection procured by the dialect,
so SQL query is not invoked when this accessor is called.
.. versionadded:: 0.9.9
.. seealso::
:meth:`.Connection.get_isolation_level` - view current level
:paramref:`.create_engine.isolation_level`
- set per :class:`.Engine` isolation level
:paramref:`.Connection.execution_options.isolation_level`
- set per :class:`.Connection` isolation level
"""
return self.dialect.default_isolation_level
def _revalidate_connection(self):
if self.__branch_from:
return self.__branch_from._revalidate_connection()
if self.__can_reconnect and self.__invalid:
if self.__transaction is not None:
raise exc.InvalidRequestError(
"Can't reconnect until invalid "
"transaction is rolled back")
self.__connection = self.engine.raw_connection()
self.__connection = self.engine.raw_connection(_connection=self)
self.__invalid = False
return self.__connection
raise exc.ResourceClosedError("This Connection is closed")
@ -343,16 +492,17 @@ class Connection(Connectable):
:ref:`pool_connection_invalidation`
"""
if self.invalidated:
return
if self.closed:
raise exc.ResourceClosedError("This Connection is closed")
if self._connection_is_valid:
self.__connection.invalidate(exception)
del self.__connection
self.__invalid = True
if self._root._connection_is_valid:
self._root.__connection.invalidate(exception)
del self._root.__connection
self._root.__invalid = True
def detach(self):
"""Detach the underlying DB-API connection from its connection pool.
@ -415,6 +565,8 @@ class Connection(Connectable):
:class:`.Engine`.
"""
if self.__branch_from:
return self.__branch_from.begin()
if self.__transaction is None:
self.__transaction = RootTransaction(self)
@ -436,6 +588,9 @@ class Connection(Connectable):
See also :meth:`.Connection.begin`,
:meth:`.Connection.begin_twophase`.
"""
if self.__branch_from:
return self.__branch_from.begin_nested()
if self.__transaction is None:
self.__transaction = RootTransaction(self)
else:
@ -459,6 +614,9 @@ class Connection(Connectable):
"""
if self.__branch_from:
return self.__branch_from.begin_twophase(xid=xid)
if self.__transaction is not None:
raise exc.InvalidRequestError(
"Cannot start a two phase transaction when a transaction "
@ -479,10 +637,11 @@ class Connection(Connectable):
def in_transaction(self):
"""Return True if a transaction is in progress."""
return self.__transaction is not None
return self._root.__transaction is not None
def _begin_impl(self, transaction):
assert not self.__branch_from
if self._echo:
self.engine.logger.info("BEGIN (implicit)")
@ -497,6 +656,8 @@ class Connection(Connectable):
self._handle_dbapi_exception(e, None, None, None, None)
def _rollback_impl(self):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.rollback(self)
@ -516,6 +677,8 @@ class Connection(Connectable):
self.__transaction = None
def _commit_impl(self, autocommit=False):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.commit(self)
@ -532,6 +695,8 @@ class Connection(Connectable):
self.__transaction = None
def _savepoint_impl(self, name=None):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.savepoint(self, name)
@ -543,6 +708,8 @@ class Connection(Connectable):
return name
def _rollback_to_savepoint_impl(self, name, context):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.rollback_savepoint(self, name, context)
@ -551,6 +718,8 @@ class Connection(Connectable):
self.__transaction = context
def _release_savepoint_impl(self, name, context):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.release_savepoint(self, name, context)
@ -559,6 +728,8 @@ class Connection(Connectable):
self.__transaction = context
def _begin_twophase_impl(self, transaction):
assert not self.__branch_from
if self._echo:
self.engine.logger.info("BEGIN TWOPHASE (implicit)")
if self._has_events or self.engine._has_events:
@ -571,6 +742,8 @@ class Connection(Connectable):
self.connection._reset_agent = transaction
def _prepare_twophase_impl(self, xid):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.prepare_twophase(self, xid)
@ -579,6 +752,8 @@ class Connection(Connectable):
self.engine.dialect.do_prepare_twophase(self, xid)
def _rollback_twophase_impl(self, xid, is_prepared):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.rollback_twophase(self, xid, is_prepared)
@ -595,6 +770,8 @@ class Connection(Connectable):
self.__transaction = None
def _commit_twophase_impl(self, xid, is_prepared):
assert not self.__branch_from
if self._has_events or self.engine._has_events:
self.dispatch.commit_twophase(self, xid, is_prepared)
@ -610,8 +787,8 @@ class Connection(Connectable):
self.__transaction = None
def _autorollback(self):
if not self.in_transaction():
self._rollback_impl()
if not self._root.in_transaction():
self._root._rollback_impl()
def close(self):
"""Close this :class:`.Connection`.
@ -632,13 +809,21 @@ class Connection(Connectable):
and will allow no further operations.
"""
if self.__branch_from:
try:
del self.__connection
except AttributeError:
pass
finally:
self.__can_reconnect = False
return
try:
conn = self.__connection
except AttributeError:
pass
else:
if not self.__branch:
conn.close()
conn.close()
if conn._reset_agent is self.__transaction:
conn._reset_agent = None
@ -670,7 +855,7 @@ class Connection(Connectable):
a subclass of :class:`.Executable`, such as a
:func:`~.expression.select` construct
* a :class:`.FunctionElement`, such as that generated
by :attr:`.func`, will be automatically wrapped in
by :data:`.func`, will be automatically wrapped in
a SELECT statement, which is then executed.
* a :class:`.DDLElement` object
* a :class:`.DefaultGenerator` object
@ -798,17 +983,16 @@ class Connection(Connectable):
distilled_params = _distill_params(multiparams, params)
if distilled_params:
# note this is usually dict but we support RowProxy
# as well; but dict.keys() as an iterator is OK
# as well; but dict.keys() as an iterable is OK
keys = distilled_params[0].keys()
else:
keys = []
dialect = self.dialect
if 'compiled_cache' in self._execution_options:
key = dialect, elem, tuple(keys), len(distilled_params) > 1
if key in self._execution_options['compiled_cache']:
compiled_sql = self._execution_options['compiled_cache'][key]
else:
key = dialect, elem, tuple(sorted(keys)), len(distilled_params) > 1
compiled_sql = self._execution_options['compiled_cache'].get(key)
if compiled_sql is None:
compiled_sql = elem.compile(
dialect=dialect, column_keys=keys,
inline=len(distilled_params) > 1)
@ -888,9 +1072,10 @@ class Connection(Connectable):
context = constructor(dialect, self, conn, *args)
except Exception as e:
self._handle_dbapi_exception(e,
util.text_type(statement), parameters,
None, None)
self._handle_dbapi_exception(
e,
util.text_type(statement), parameters,
None, None)
if context.compiled:
context.pre_exec()
@ -914,36 +1099,39 @@ class Connection(Connectable):
"%r",
sql_util._repr_params(parameters, batches=10)
)
evt_handled = False
try:
if context.executemany:
for fn in () if not self.dialect._has_events \
else self.dialect.dispatch.do_executemany:
if fn(cursor, statement, parameters, context):
break
else:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_executemany:
if fn(cursor, statement, parameters, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_executemany(
cursor,
statement,
parameters,
context)
elif not parameters and context.no_parameters:
for fn in () if not self.dialect._has_events \
else self.dialect.dispatch.do_execute_no_params:
if fn(cursor, statement, context):
break
else:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_execute_no_params:
if fn(cursor, statement, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_execute_no_params(
cursor,
statement,
context)
else:
for fn in () if not self.dialect._has_events \
else self.dialect.dispatch.do_execute:
if fn(cursor, statement, parameters, context):
break
else:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_execute:
if fn(cursor, statement, parameters, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_execute(
cursor,
statement,
@ -967,36 +1155,17 @@ class Connection(Connectable):
if context.compiled:
context.post_exec()
if context.isinsert and not context.executemany:
context.post_insert()
if context.is_crud or context.is_text:
result = context._setup_crud_result_proxy()
else:
result = context.get_result_proxy()
if result._metadata is None:
result._soft_close(_autoclose_connection=False)
# create a resultproxy, get rowcount/implicit RETURNING
# rows, close cursor if no further results pending
result = context.get_result_proxy()
if context.isinsert:
if context._is_implicit_returning:
context._fetch_implicit_returning(result)
result.close(_autoclose_connection=False)
result._metadata = None
elif not context._is_explicit_returning:
result.close(_autoclose_connection=False)
result._metadata = None
elif context.isupdate and context._is_implicit_returning:
context._fetch_implicit_update_returning(result)
result.close(_autoclose_connection=False)
result._metadata = None
if context.should_autocommit and self._root.__transaction is None:
self._root._commit_impl(autocommit=True)
elif result._metadata is None:
# no results, get rowcount
# (which requires open cursor on some drivers
# such as kintersbasdb, mxodbc),
result.rowcount
result.close(_autoclose_connection=False)
if self.__transaction is None and context.should_autocommit:
self._commit_impl(autocommit=True)
if result.closed and self.should_close_with_result:
if result._soft_closed and self.should_close_with_result:
self.close()
return result
@ -1055,8 +1224,6 @@ class Connection(Connectable):
"""
try:
cursor.close()
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
# log the error through the connection pool's logger.
self.engine.pool.logger.error(
@ -1071,7 +1238,6 @@ class Connection(Connectable):
parameters,
cursor,
context):
exc_info = sys.exc_info()
if context and context.exception is None:
@ -1081,16 +1247,22 @@ class Connection(Connectable):
self._is_disconnect = \
isinstance(e, self.dialect.dbapi.Error) and \
not self.closed and \
self.dialect.is_disconnect(e, self.__connection, cursor)
self.dialect.is_disconnect(
e,
self.__connection if not self.invalidated else None,
cursor)
if context:
context.is_disconnect = self._is_disconnect
invalidate_pool_on_disconnect = True
if self._reentrant_error:
util.raise_from_cause(
exc.DBAPIError.instance(statement,
parameters,
e,
self.dialect.dbapi.Error),
self.dialect.dbapi.Error,
dialect=self.dialect),
exc_info
)
self._reentrant_error = True
@ -1106,13 +1278,16 @@ class Connection(Connectable):
parameters,
e,
self.dialect.dbapi.Error,
connection_invalidated=self._is_disconnect)
connection_invalidated=self._is_disconnect,
dialect=self.dialect)
else:
sqlalchemy_exception = None
newraise = None
if self._has_events or self.engine._has_events:
if (self._has_events or self.engine._has_events) and \
not self._execution_options.get(
'skip_user_error_events', False):
# legacy dbapi_error event
if should_wrap and context:
self.dispatch.dbapi_error(self,
@ -1124,7 +1299,8 @@ class Connection(Connectable):
# new handle_error event
ctx = ExceptionContextImpl(
e, sqlalchemy_exception, self, cursor, statement,
e, sqlalchemy_exception, self.engine,
self, cursor, statement,
parameters, context, self._is_disconnect)
for fn in self.dispatch.handle_error:
@ -1144,6 +1320,11 @@ class Connection(Connectable):
sqlalchemy_exception.connection_invalidated = \
self._is_disconnect = ctx.is_disconnect
# set up potentially user-defined value for
# invalidate pool.
invalidate_pool_on_disconnect = \
ctx.invalidate_pool_on_disconnect
if should_wrap and context:
context.handle_dbapi_exception(e)
@ -1166,12 +1347,66 @@ class Connection(Connectable):
del self._reentrant_error
if self._is_disconnect:
del self._is_disconnect
dbapi_conn_wrapper = self.connection
self.engine.pool._invalidate(dbapi_conn_wrapper, e)
self.invalidate(e)
if not self.invalidated:
dbapi_conn_wrapper = self.__connection
if invalidate_pool_on_disconnect:
self.engine.pool._invalidate(dbapi_conn_wrapper, e)
self.invalidate(e)
if self.should_close_with_result:
self.close()
@classmethod
def _handle_dbapi_exception_noconnection(cls, e, dialect, engine):
exc_info = sys.exc_info()
is_disconnect = dialect.is_disconnect(e, None, None)
should_wrap = isinstance(e, dialect.dbapi.Error)
if should_wrap:
sqlalchemy_exception = exc.DBAPIError.instance(
None,
None,
e,
dialect.dbapi.Error,
connection_invalidated=is_disconnect)
else:
sqlalchemy_exception = None
newraise = None
if engine._has_events:
ctx = ExceptionContextImpl(
e, sqlalchemy_exception, engine, None, None, None,
None, None, is_disconnect)
for fn in engine.dispatch.handle_error:
try:
# handler returns an exception;
# call next handler in a chain
per_fn = fn(ctx)
if per_fn is not None:
ctx.chained_exception = newraise = per_fn
except Exception as _raised:
# handler raises an exception - stop processing
newraise = _raised
break
if sqlalchemy_exception and \
is_disconnect != ctx.is_disconnect:
sqlalchemy_exception.connection_invalidated = \
is_disconnect = ctx.is_disconnect
if newraise:
util.raise_from_cause(newraise, exc_info)
elif should_wrap:
util.raise_from_cause(
sqlalchemy_exception,
exc_info
)
else:
util.reraise(*exc_info)
def default_schema_name(self):
return self.engine.dialect.get_default_schema_name(self)
@ -1250,8 +1485,9 @@ class ExceptionContextImpl(ExceptionContext):
"""Implement the :class:`.ExceptionContext` interface."""
def __init__(self, exception, sqlalchemy_exception,
connection, cursor, statement, parameters,
engine, connection, cursor, statement, parameters,
context, is_disconnect):
self.engine = engine
self.connection = connection
self.sqlalchemy_exception = sqlalchemy_exception
self.original_exception = exception
@ -1295,9 +1531,13 @@ class Transaction(object):
def __init__(self, connection, parent):
self.connection = connection
self._parent = parent or self
self._actual_parent = parent
self.is_active = True
@property
def _parent(self):
return self._actual_parent or self
def close(self):
"""Close this :class:`.Transaction`.
@ -1575,29 +1815,28 @@ class Engine(Connectable, log.Identified):
def dispose(self):
"""Dispose of the connection pool used by this :class:`.Engine`.
This has the effect of fully closing all **currently checked in**
database connections. Connections that are still checked out
will **not** be closed, however they will no longer be associated
with this :class:`.Engine`, so when they are closed individually,
eventually the :class:`.Pool` which they are associated with will
be garbage collected and they will be closed out fully, if
not already closed on checkin.
A new connection pool is created immediately after the old one has
been disposed. This new pool, like all SQLAlchemy connection pools,
does not make any actual connections to the database until one is
first requested.
first requested, so as long as the :class:`.Engine` isn't used again,
no new connections will be made.
This method has two general use cases:
.. seealso::
* When a dropped connection is detected, it is assumed that all
connections held by the pool are potentially dropped, and
the entire pool is replaced.
* An application may want to use :meth:`dispose` within a test
suite that is creating multiple engines.
It is critical to note that :meth:`dispose` does **not** guarantee
that the application will release all open database connections - only
those connections that are checked into the pool are closed.
Connections which remain checked out or have been detached from
the engine are not affected.
:ref:`engine_disposal`
"""
self.pool.dispose()
self.pool = self.pool.recreate()
self.dispatch.engine_disposed(self)
def _execute_default(self, default):
with self.contextual_connect() as conn:
@ -1795,10 +2034,11 @@ class Engine(Connectable, log.Identified):
"""
return self._connection_cls(self,
self.pool.connect(),
close_with_result=close_with_result,
**kwargs)
return self._connection_cls(
self,
self._wrap_pool_connect(self.pool.connect, None),
close_with_result=close_with_result,
**kwargs)
def table_names(self, schema=None, connection=None):
"""Return a list of all table names available in the database.
@ -1828,7 +2068,18 @@ class Engine(Connectable, log.Identified):
"""
return self.run_callable(self.dialect.has_table, table_name, schema)
def raw_connection(self):
def _wrap_pool_connect(self, fn, connection):
dialect = self.dialect
try:
return fn()
except dialect.dbapi.Error as e:
if connection is None:
Connection._handle_dbapi_exception_noconnection(
e, dialect, self)
else:
util.reraise(*sys.exc_info())
def raw_connection(self, _connection=None):
"""Return a "raw" DBAPI connection from the connection pool.
The returned object is a proxied version of the DBAPI
@ -1839,13 +2090,18 @@ class Engine(Connectable, log.Identified):
for real.
This method provides direct DBAPI connection access for
special situations. In most situations, the :class:`.Connection`
object should be used, which is procured using the
:meth:`.Engine.connect` method.
special situations when the API provided by :class:`.Connection`
is not needed. When a :class:`.Connection` object is already
present, the DBAPI connection is available using
the :attr:`.Connection.connection` accessor.
.. seealso::
:ref:`dbapi_connections`
"""
return self.pool.unique_connection()
return self._wrap_pool_connect(
self.pool.unique_connection, _connection)
class OptionEngine(Engine):