update sqlalchemy
This commit is contained in:
parent
7365367c61
commit
3b436646a2
362 changed files with 37720 additions and 11021 deletions
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue