summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/cextension/resultproxy.c14
-rw-r--r--lib/sqlalchemy/engine/__init__.py2
-rw-r--r--lib/sqlalchemy/engine/base.py137
-rw-r--r--lib/sqlalchemy/engine/cursor.py243
-rw-r--r--lib/sqlalchemy/engine/default.py41
-rw-r--r--lib/sqlalchemy/engine/events.py9
-rw-r--r--lib/sqlalchemy/engine/interfaces.py57
-rw-r--r--lib/sqlalchemy/engine/mock.py24
-rw-r--r--lib/sqlalchemy/engine/result.py21
-rw-r--r--lib/sqlalchemy/engine/row.py210
-rw-r--r--lib/sqlalchemy/sql/base.py40
-rw-r--r--lib/sqlalchemy/sql/compiler.py2
-rw-r--r--lib/sqlalchemy/sql/schema.py13
-rw-r--r--lib/sqlalchemy/testing/assertions.py6
-rw-r--r--lib/sqlalchemy/testing/profiling.py7
-rw-r--r--lib/sqlalchemy/testing/suite/test_select.py4
16 files changed, 111 insertions, 719 deletions
diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c
index 2de672f22..99b2d36f3 100644
--- a/lib/sqlalchemy/cextension/resultproxy.c
+++ b/lib/sqlalchemy/cextension/resultproxy.c
@@ -55,8 +55,6 @@ static PyObject *sqlalchemy_engine_result = NULL;
static int KEY_INTEGER_ONLY = 0;
static int KEY_OBJECTS_ONLY = 1;
-static int KEY_OBJECTS_BUT_WARN = 2;
-//static int KEY_OBJECTS_NO_WARN = 3;
/****************
* BaseRow *
@@ -409,16 +407,6 @@ BaseRow_getitem_by_object(BaseRow *self, PyObject *key, int asmapping)
/* -1 can be either the actual value, or an error flag. */
return NULL;
- if (!asmapping && self->key_style == KEY_OBJECTS_BUT_WARN) {
- PyObject *tmp;
-
- tmp = PyObject_CallMethod(self->parent, "_warn_for_nonint", "O", key);
- if (tmp == NULL) {
- return NULL;
- }
- Py_DECREF(tmp);
- }
-
return BaseRow_getitem(self, index);
}
@@ -503,7 +491,7 @@ BaseRow_subscript(BaseRow *self, PyObject *key)
static PyObject *
BaseRow_subscript_mapping(BaseRow *self, PyObject *key)
{
- if (self->key_style == KEY_OBJECTS_BUT_WARN || self->key_style == KEY_INTEGER_ONLY) {
+ if (self->key_style == KEY_INTEGER_ONLY) {
return BaseRow_subscript_impl(self, key, 0);
}
else {
diff --git a/lib/sqlalchemy/engine/__init__.py b/lib/sqlalchemy/engine/__init__.py
index 6306e201d..ba57eee51 100644
--- a/lib/sqlalchemy/engine/__init__.py
+++ b/lib/sqlalchemy/engine/__init__.py
@@ -31,7 +31,6 @@ from .cursor import BufferedColumnRow
from .cursor import BufferedRowResultProxy
from .cursor import CursorResult
from .cursor import FullyBufferedResultProxy
-from .cursor import LegacyCursorResult
from .cursor import ResultProxy
from .interfaces import AdaptedConnection
from .interfaces import Compiled
@@ -52,7 +51,6 @@ from .result import Result
from .result import result_tuple
from .result import ScalarResult
from .row import BaseRow
-from .row import LegacyRow
from .row import Row
from .row import RowMapping
from .url import make_url
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 2444b5c7f..c60a8383f 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -10,6 +10,7 @@ import contextlib
import sys
from .interfaces import Connectable
+from .interfaces import ConnectionEventsTarget
from .interfaces import ExceptionContext
from .util import _distill_params
from .util import _distill_params_20
@@ -64,11 +65,15 @@ class Connection(Connectable):
# used by sqlalchemy.engine.util.TransactionalContext
_trans_context_manager = None
+ # legacy as of 2.0, should be eventually deprecated and
+ # removed. was used in the "pre_ping" recipe that's been in the docs
+ # a long time
+ should_close_with_result = False
+
def __init__(
self,
engine,
connection=None,
- close_with_result=False,
_branch_from=None,
_execution_options=None,
_dispatch=None,
@@ -86,7 +91,6 @@ class Connection(Connectable):
self._dbapi_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:
@@ -99,7 +103,6 @@ class Connection(Connectable):
self._transaction = self._nested_transaction = None
self.__savepoint_seq = 0
self.__in_begin = False
- self.should_close_with_result = close_with_result
self.__can_reconnect = _allow_revalidate
self._echo = self.engine._should_log_info()
@@ -169,8 +172,7 @@ class Connection(Connectable):
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.
+ engine and connection; whose close() method does nothing.
.. deprecated:: 1.4 the "branching" concept will be removed in
SQLAlchemy 2.0 as well as the "Connection.connect()" method which
@@ -590,7 +592,9 @@ class Connection(Connectable):
return self.connection.info
@util.deprecated_20(":meth:`.Connection.connect`")
- def connect(self, close_with_result=False):
+ def connect(
+ self,
+ ):
"""Returns a branched version of this :class:`_engine.Connection`.
The :meth:`_engine.Connection.close` method on the returned
@@ -1333,8 +1337,6 @@ class Connection(Connectable):
self._handle_dbapi_exception(e, None, None, None, None)
ret = ctx._exec_default(None, default, None)
- if self.should_close_with_result:
- self.close()
if self._has_events or self.engine._has_events:
self.dispatch.after_execute(
@@ -1684,7 +1686,6 @@ class Connection(Connectable):
"""Create an :class:`.ExecutionContext` and execute, returning
a :class:`_engine.CursorResult`."""
- branched = self
if self.__branch_from:
# if this is a "branched" connection, do everything in terms
# of the "root" connection, *except* for .close(), which is
@@ -1705,6 +1706,7 @@ class Connection(Connectable):
self._handle_dbapi_exception(
e, util.text_type(statement), parameters, None, None
)
+ return # not reached
if (
self._transaction
@@ -1815,10 +1817,6 @@ class Connection(Connectable):
result = context._setup_result_proxy()
if not self._is_future:
- should_close_with_result = branched.should_close_with_result
-
- if not result._soft_closed and should_close_with_result:
- result._autoclose_connection = True
if (
# usually we're in a transaction so avoid relatively
@@ -1828,16 +1826,6 @@ class Connection(Connectable):
):
self._commit_impl(autocommit=True)
- # for "connectionless" execution, we have to close this
- # Connection after the statement is complete.
- # legacy stuff.
- if should_close_with_result and context._soft_closed:
- assert not self._is_future
-
- # CursorResult already exhausted rows / has no rows.
- # close us now
- branched.close()
-
except BaseException as e:
self._handle_dbapi_exception(
e, statement, parameters, cursor, context
@@ -2035,9 +2023,6 @@ class Connection(Connectable):
if invalidate_pool_on_disconnect:
self.engine.pool._invalidate(dbapi_conn_wrapper, e)
self.invalidate(e)
- if self.should_close_with_result:
- assert not self._is_future
- self.close()
@classmethod
def _handle_dbapi_exception_noconnection(cls, e, dialect, engine):
@@ -2710,7 +2695,7 @@ class TwoPhaseTransaction(RootTransaction):
self.connection._commit_twophase_impl(self.xid, self._is_prepared)
-class Engine(Connectable, log.Identified):
+class Engine(ConnectionEventsTarget, log.Identified):
"""
Connects a :class:`~sqlalchemy.pool.Pool` and
:class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a
@@ -2965,10 +2950,9 @@ class Engine(Connectable, log.Identified):
yield connection
class _trans_ctx(object):
- def __init__(self, conn, transaction, close_with_result):
+ def __init__(self, conn, transaction):
self.conn = conn
self.transaction = transaction
- self.close_with_result = close_with_result
def __enter__(self):
self.transaction.__enter__()
@@ -2978,10 +2962,9 @@ class Engine(Connectable, log.Identified):
try:
self.transaction.__exit__(type_, value, traceback)
finally:
- if not self.close_with_result:
- self.conn.close()
+ self.conn.close()
- def begin(self, close_with_result=False):
+ def begin(self):
"""Return a context manager delivering a :class:`_engine.Connection`
with a :class:`.Transaction` established.
@@ -2997,15 +2980,6 @@ class Engine(Connectable, log.Identified):
is committed. If an error is raised, the :class:`.Transaction`
is rolled back.
- Legacy use only: the ``close_with_result`` flag is normally ``False``,
- and indicates that the :class:`_engine.Connection` will be closed when
- the operation is complete. When set to ``True``, it indicates the
- :class:`_engine.Connection` is in "single use" mode, where the
- :class:`_engine.CursorResult` returned by the first call to
- :meth:`_engine.Connection.execute` will close the
- :class:`_engine.Connection` when that :class:`_engine.CursorResult` has
- exhausted all result rows.
-
.. seealso::
:meth:`_engine.Engine.connect` - procure a
@@ -3016,16 +2990,13 @@ class Engine(Connectable, log.Identified):
for a particular :class:`_engine.Connection`.
"""
- if self._connection_cls._is_future:
- conn = self.connect()
- else:
- conn = self.connect(close_with_result=close_with_result)
+ conn = self.connect()
try:
trans = conn.begin()
except:
with util.safe_reraise():
conn.close()
- return Engine._trans_ctx(conn, trans, close_with_result)
+ return Engine._trans_ctx(conn, trans)
@util.deprecated(
"1.4",
@@ -3106,77 +3077,7 @@ class Engine(Connectable, log.Identified):
with self.begin() as conn:
conn._run_ddl_visitor(visitorcallable, element, **kwargs)
- @util.deprecated_20(
- ":meth:`_engine.Engine.execute`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`.",
- )
- def execute(self, statement, *multiparams, **params):
- """Executes the given construct and returns a
- :class:`_engine.CursorResult`.
-
- The arguments are the same as those used by
- :meth:`_engine.Connection.execute`.
-
- Here, a :class:`_engine.Connection` is acquired using the
- :meth:`_engine.Engine.connect` method, and the statement executed
- with that connection. The returned :class:`_engine.CursorResult`
- is flagged
- such that when the :class:`_engine.CursorResult` is exhausted and its
- underlying cursor is closed, the :class:`_engine.Connection`
- created here
- will also be closed, which allows its associated DBAPI connection
- resource to be returned to the connection pool.
-
- """
- connection = self.connect(close_with_result=True)
- return connection.execute(statement, *multiparams, **params)
-
- @util.deprecated_20(
- ":meth:`_engine.Engine.scalar`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`; the :meth:`_future.Result.scalar` "
- "method can then be "
- "used to return a scalar result.",
- )
- def scalar(self, statement, *multiparams, **params):
- """Executes and returns the first column of the first row.
-
- The underlying result/cursor is closed after execution.
- """
- return self.execute(statement, *multiparams, **params).scalar()
-
- def _execute_clauseelement(
- self,
- elem,
- multiparams=None,
- params=None,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- connection = self.connect(close_with_result=True)
- return connection._execute_clauseelement(
- elem, multiparams, params, execution_options
- )
-
- def _execute_compiled(
- self,
- compiled,
- multiparams,
- params,
- execution_options=_EMPTY_EXECUTION_OPTS,
- ):
- connection = self.connect(close_with_result=True)
- return connection._execute_compiled(
- compiled, multiparams, params, execution_options
- )
-
- def connect(self, close_with_result=False):
+ def connect(self):
"""Return a new :class:`_engine.Connection` object.
The :class:`_engine.Connection` object is a facade that uses a DBAPI
@@ -3191,7 +3092,7 @@ class Engine(Connectable, log.Identified):
"""
- return self._connection_cls(self, close_with_result=close_with_result)
+ return self._connection_cls(self)
@util.deprecated(
"1.4",
diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py
index 5e6078f86..049422617 100644
--- a/lib/sqlalchemy/engine/cursor.py
+++ b/lib/sqlalchemy/engine/cursor.py
@@ -16,10 +16,10 @@ from .result import Result
from .result import ResultMetaData
from .result import SimpleResultMetaData
from .result import tuplegetter
-from .row import LegacyRow
+from .row import Row
from .. import exc
from .. import util
-from ..sql import expression
+from ..sql import elements
from ..sql import sqltypes
from ..sql import util as sql_util
from ..sql.base import _generative
@@ -53,7 +53,8 @@ class CursorResultMetaData(ResultMetaData):
"_keymap_by_result_column_idx",
"_tuplefilter",
"_translated_indexes",
- "_safe_for_cache"
+ "_safe_for_cache",
+ "_unpickled"
# don't need _unique_filters support here for now. Can be added
# if a need arises.
)
@@ -82,6 +83,7 @@ class CursorResultMetaData(ResultMetaData):
new_metadata = self.__class__.__new__(self.__class__)
new_metadata.case_sensitive = self.case_sensitive
+ new_metadata._unpickled = self._unpickled
new_metadata._processors = self._processors
new_metadata._keys = new_keys
new_metadata._tuplefilter = tup
@@ -143,6 +145,7 @@ class CursorResultMetaData(ResultMetaData):
md._keymap[new] = rec
md.case_sensitive = self.case_sensitive
+ md._unpickled = self._unpickled
md._processors = self._processors
assert not self._tuplefilter
md._tuplefilter = None
@@ -158,7 +161,7 @@ class CursorResultMetaData(ResultMetaData):
self._tuplefilter = None
self._translated_indexes = None
self.case_sensitive = dialect.case_sensitive
- self._safe_for_cache = False
+ self._safe_for_cache = self._unpickled = False
if context.result_column_struct:
(
@@ -610,14 +613,34 @@ class CursorResultMetaData(ResultMetaData):
)
def _key_fallback(self, key, err, raiseerr=True):
+
+ # we apparently have not marked .case_sensitive as
+ # RemovedIn20. I still think we should remove it as I can't
+ # imagine anyone is using it, however lets make that a separate
+ # commit.
+ if not self.case_sensitive and isinstance(key, util.string_types):
+ map_ = self._keymap
+ result = map_.get(key.lower())
+ if result is not None:
+ return result
+
if raiseerr:
- util.raise_(
- exc.NoSuchColumnError(
- "Could not locate column in row for column '%s'"
- % util.string_or_unprintable(key)
- ),
- replace_context=err,
- )
+ if self._unpickled and isinstance(key, elements.ColumnElement):
+ util.raise_(
+ exc.NoSuchColumnError(
+ "Row was unpickled; lookup by ColumnElement "
+ "is unsupported"
+ ),
+ replace_context=err,
+ )
+ else:
+ util.raise_(
+ exc.NoSuchColumnError(
+ "Could not locate column in row for column '%s'"
+ % util.string_or_unprintable(key)
+ ),
+ replace_context=err,
+ )
else:
return None
@@ -694,7 +717,7 @@ class CursorResultMetaData(ResultMetaData):
}
self._keys = state["_keys"]
self.case_sensitive = state["case_sensitive"]
-
+ self._unpickled = True
if state["_translated_indexes"]:
self._translated_indexes = state["_translated_indexes"]
self._tuplefilter = tuplegetter(*self._translated_indexes)
@@ -702,116 +725,6 @@ class CursorResultMetaData(ResultMetaData):
self._translated_indexes = self._tuplefilter = None
-class LegacyCursorResultMetaData(CursorResultMetaData):
- __slots__ = ()
-
- def _contains(self, value, row):
- key = value
- if key in self._keymap:
- util.warn_deprecated_20(
- "Using the 'in' operator to test for string or column "
- "keys, or integer indexes, in a :class:`.Row` object is "
- "deprecated and will "
- "be removed in a future release. "
- "Use the `Row._fields` or `Row._mapping` attribute, i.e. "
- "'key in row._fields'",
- )
- return True
- else:
- return self._key_fallback(key, None, False) is not None
-
- def _key_fallback(self, key, err, raiseerr=True):
- map_ = self._keymap
- result = None
-
- if isinstance(key, util.string_types):
- result = map_.get(key if self.case_sensitive else key.lower())
- elif isinstance(key, expression.ColumnElement):
- if (
- key._tq_label
- and (
- key._tq_label
- if self.case_sensitive
- else key._tq_label.lower()
- )
- in map_
- ):
- result = map_[
- key._tq_label
- if self.case_sensitive
- else key._tq_label.lower()
- ]
- elif (
- hasattr(key, "name")
- and (key.name if self.case_sensitive else key.name.lower())
- in map_
- ):
- # match is only on name.
- result = map_[
- key.name if self.case_sensitive else key.name.lower()
- ]
-
- # search extra hard to make sure this
- # isn't a column/label name overlap.
- # this check isn't currently available if the row
- # was unpickled.
- if result is not None and result[MD_OBJECTS] not in (
- None,
- _UNPICKLED,
- ):
- for obj in result[MD_OBJECTS]:
- if key._compare_name_for_result(obj):
- break
- else:
- result = None
- if result is not None:
- if result[MD_OBJECTS] is _UNPICKLED:
- util.warn_deprecated(
- "Retrieving row values using Column objects from a "
- "row that was unpickled is deprecated; adequate "
- "state cannot be pickled for this to be efficient. "
- "This usage will raise KeyError in a future release.",
- version="1.4",
- )
- else:
- util.warn_deprecated(
- "Retrieving row values using Column objects with only "
- "matching names as keys is deprecated, and will raise "
- "KeyError in a future release; only Column "
- "objects that are explicitly part of the statement "
- "object should be used.",
- version="1.4",
- )
- if result is None:
- if raiseerr:
- util.raise_(
- exc.NoSuchColumnError(
- "Could not locate column in row for column '%s'"
- % util.string_or_unprintable(key)
- ),
- replace_context=err,
- )
- else:
- return None
- else:
- map_[key] = result
- return result
-
- def _warn_for_nonint(self, key):
- util.warn_deprecated_20(
- "Using non-integer/slice indices on Row is deprecated and will "
- "be removed in version 2.0; please use row._mapping[<key>], or "
- "the mappings() accessor on the Result object.",
- stacklevel=4,
- )
-
- def _has_key(self, key):
- if key in self._keymap:
- return True
- else:
- return self._key_fallback(key, None, False) is not None
-
-
class ResultFetchStrategy(object):
"""Define a fetching strategy for a result object.
@@ -1205,19 +1118,7 @@ class _NoResultMetaData(ResultMetaData):
self._we_dont_return_rows()
-class _LegacyNoResultMetaData(_NoResultMetaData):
- @property
- def keys(self):
- util.warn_deprecated_20(
- "Calling the .keys() method on a result set that does not return "
- "rows is deprecated and will raise ResourceClosedError in "
- "SQLAlchemy 2.0.",
- )
- return []
-
-
_NO_RESULT_METADATA = _NoResultMetaData()
-_LEGACY_NO_RESULT_METADATA = _LegacyNoResultMetaData()
class BaseCursorResult(object):
@@ -1750,10 +1651,9 @@ class BaseCursorResult(object):
class CursorResult(BaseCursorResult, Result):
"""A Result that is representing state from a DBAPI cursor.
- .. versionchanged:: 1.4 The :class:`.CursorResult` and
- :class:`.LegacyCursorResult`
- classes replace the previous :class:`.ResultProxy` interface.
- These classes are based on the :class:`.Result` calling API
+ .. versionchanged:: 1.4 The :class:`.CursorResult``
+ class replaces the previous :class:`.ResultProxy` interface.
+ This classes are based on the :class:`.Result` calling API
which provides an updated usage model and calling facade for
SQLAlchemy Core and SQLAlchemy ORM.
@@ -1762,14 +1662,6 @@ class CursorResult(BaseCursorResult, Result):
the DBAPI. Through the use of filters such as the :meth:`.Result.scalars`
method, other kinds of objects may also be returned.
- Within the scope of the 1.x series of SQLAlchemy, Core SQL results in
- version 1.4 return an instance of :class:`._engine.LegacyCursorResult`
- which takes the place of the ``CursorResult`` class used for the 1.3 series
- and previously. This object returns rows as :class:`.LegacyRow` objects,
- which maintains Python mapping (i.e. dictionary) like behaviors upon the
- object itself. Going forward, the :attr:`.Row._mapping` attribute should
- be used for dictionary behaviors.
-
.. seealso::
:ref:`coretutorial_selecting` - introductory material for accessing
@@ -1839,62 +1731,7 @@ class CursorResult(BaseCursorResult, Result):
self.cursor_strategy.yield_per(self, self.cursor, num)
-class LegacyCursorResult(CursorResult):
- """Legacy version of :class:`.CursorResult`.
-
- This class includes connection "connection autoclose" behavior for use with
- "connectionless" execution, as well as delivers rows using the
- :class:`.LegacyRow` row implementation.
-
- .. versionadded:: 1.4
-
- """
-
- _autoclose_connection = False
- _process_row = LegacyRow
- _cursor_metadata = LegacyCursorResultMetaData
- _cursor_strategy_cls = CursorFetchStrategy
-
- _no_result_metadata = _LEGACY_NO_RESULT_METADATA
-
- def close(self):
- """Close this :class:`_engine.LegacyCursorResult`.
-
- This method has the same behavior as that of
- :meth:`._engine.CursorResult`, but it also may close
- the underlying :class:`.Connection` for the case of "connectionless"
- execution.
-
- .. deprecated:: 2.0 "connectionless" execution is deprecated and will
- be removed in version 2.0. Version 2.0 will feature the
- :class:`_future.Result`
- object that will no longer affect the status
- of the originating connection in any case.
-
- After this method is called, it is no longer valid to call upon
- the fetch methods, which will raise a :class:`.ResourceClosedError`
- on subsequent use.
-
- .. seealso::
-
- :ref:`connections_toplevel`
-
- :ref:`dbengine_implicit`
- """
- self._soft_close(hard=True)
-
- def _soft_close(self, hard=False):
- soft_closed = self._soft_closed
- super(LegacyCursorResult, self)._soft_close(hard=hard)
- if (
- not soft_closed
- and self._soft_closed
- and self._autoclose_connection
- ):
- self.connection.close()
-
-
-ResultProxy = LegacyCursorResult
+ResultProxy = CursorResult
class BufferedRowResultProxy(ResultProxy):
@@ -1919,7 +1756,7 @@ class FullyBufferedResultProxy(ResultProxy):
_cursor_strategy_cls = FullyBufferedCursorFetchStrategy
-class BufferedColumnRow(LegacyRow):
+class BufferedColumnRow(Row):
"""Row is now BufferedColumn in all cases"""
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index c379ec673..5046d4035 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -855,7 +855,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
_is_implicit_returning = False
_is_explicit_returning = False
- _is_future_result = False
_is_server_side = False
_soft_closed = False
@@ -890,11 +889,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.execution_options = execution_options
- self._is_future_result = (
- connection._is_future
- or self.execution_options.get("future_result", False)
- )
-
self.unicode_statement = util.text_type(compiled)
if compiled.schema_translate_map:
schema_translate_map = self.execution_options.get(
@@ -947,11 +941,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.execution_options = execution_options
- self._is_future_result = (
- connection._is_future
- or self.execution_options.get("future_result", False)
- )
-
self.result_column_struct = (
compiled._result_columns,
compiled._ordered_columns,
@@ -1106,11 +1095,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.execution_options = execution_options
- self._is_future_result = (
- connection._is_future
- or self.execution_options.get("future_result", False)
- )
-
if not parameters:
if self.dialect.positional:
self.parameters = [dialect.execute_sequence_format()]
@@ -1157,11 +1141,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.execution_options = execution_options
- self._is_future_result = (
- connection._is_future
- or self.execution_options.get("future_result", False)
- )
-
self.cursor = self.create_cursor()
return self
@@ -1420,18 +1399,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
if cursor_description is None:
strategy = _cursor._NO_CURSOR_DQL
- if self._is_future_result:
- if self.root_connection.should_close_with_result:
- raise exc.InvalidRequestError(
- "can't use future_result=True with close_with_result"
- )
- result = _cursor.CursorResult(
- self, strategy, cursor_description
- )
- else:
- result = _cursor.LegacyCursorResult(
- self, strategy, cursor_description
- )
+ result = _cursor.CursorResult(self, strategy, cursor_description)
if (
self.compiled
@@ -1493,12 +1461,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
if cursor_description is None:
strategy = _cursor._NO_CURSOR_DML
- if self._is_future_result:
- result = _cursor.CursorResult(self, strategy, cursor_description)
- else:
- result = _cursor.LegacyCursorResult(
- self, strategy, cursor_description
- )
+ result = _cursor.CursorResult(self, strategy, cursor_description)
if self.isinsert:
if self._is_implicit_returning:
diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py
index f091c7733..effebb4cb 100644
--- a/lib/sqlalchemy/engine/events.py
+++ b/lib/sqlalchemy/engine/events.py
@@ -7,20 +7,21 @@
from .base import Engine
-from .interfaces import Connectable
+from .interfaces import ConnectionEventsTarget
from .interfaces import Dialect
from .. import event
from .. import exc
class ConnectionEvents(event.Events):
- """Available events for :class:`.Connectable`, which includes
+ """Available events for
:class:`_engine.Connection` and :class:`_engine.Engine`.
The methods here define the name of an event as well as the names of
members that are passed to listener functions.
- An event listener can be associated with any :class:`.Connectable`
+ An event listener can be associated with any
+ :class:`_engine.Connection` or :class:`_engine.Engine`
class or instance, such as an :class:`_engine.Engine`, e.g.::
from sqlalchemy import event, create_engine
@@ -90,7 +91,7 @@ class ConnectionEvents(event.Events):
"""
_target_class_doc = "SomeEngine"
- _dispatch_target = Connectable
+ _dispatch_target = ConnectionEventsTarget
@classmethod
def _listen(cls, event_key, retval=False):
diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py
index fdaeaddcd..3fd245e44 100644
--- a/lib/sqlalchemy/engine/interfaces.py
+++ b/lib/sqlalchemy/engine/interfaces.py
@@ -7,7 +7,6 @@
"""Define core interfaces used by the engine system."""
-from .. import util
from ..sql.compiler import Compiled # noqa
from ..sql.compiler import TypeCompiler # noqa
@@ -1327,9 +1326,7 @@ class ExecutionContext(object):
root_connection.
root_connection
- Connection object which is the source of this ExecutionContext. This
- Connection may have close_with_result=True set, in which case it can
- only be used once.
+ Connection object which is the source of this ExecutionContext.
dialect
dialect which created this ExecutionContext.
@@ -1534,48 +1531,43 @@ class ExecutionContext(object):
raise NotImplementedError()
-@util.deprecated_20_cls(
- ":class:`.Connectable`",
- alternative=(
- "The :class:`_engine.Engine` will be the only Core "
- "object that features a .connect() method, and the "
- ":class:`_engine.Connection` will be the only object that features "
- "an .execute() method."
- ),
- constructor=None,
-)
-class Connectable(object):
- """Interface for an object which supports execution of SQL constructs.
+class ConnectionEventsTarget:
+ """An object which can accept events from :class:`.ConnectionEvents`.
- The two implementations of :class:`.Connectable` are
- :class:`_engine.Connection` and :class:`_engine.Engine`.
+ Includes :class:`_engine.Connection` and :class:`_engine.Engine`.
- Connectable must also implement the 'dialect' member which references a
- :class:`.Dialect` instance.
+ .. versionadded:: 2.0
"""
- def connect(self, **kwargs):
- """Return a :class:`_engine.Connection` object.
- Depending on context, this may be ``self`` if this object
- is already an instance of :class:`_engine.Connection`, or a newly
- procured :class:`_engine.Connection` if this object is an instance
- of :class:`_engine.Engine`.
+class Connectable(ConnectionEventsTarget):
+ """Interface for an object which supports execution of SQL constructs.
+
+ This is the base for :class:`_engine.Connection` and similar objects.
- """
+ .. versionchanged:: 2.0 :class:`_engine.Connectable` is no longer the
+ base class for :class:`_engine.Engine`, replaced with
+ :class:`_engine.ConnectionEventsTarget`.
+
+ """
engine = None
"""The :class:`_engine.Engine` instance referred to by this
:class:`.Connectable`.
- May be ``self`` if this is already an :class:`_engine.Engine`.
+ """
+
+ dialect = None
+ """The :class:`_engine.Dialect` instance referred to by this
+ :class:`.Connectable`.
"""
def execute(self, object_, *multiparams, **params):
"""Executes the given construct and returns a
- :class:`_engine.CursorResult`.
+ :class:`_result.Result`.
+
"""
raise NotImplementedError()
@@ -1583,13 +1575,8 @@ class Connectable(object):
"""Executes and returns the first column of the first row.
The underlying cursor is closed after execution.
- """
- raise NotImplementedError()
- def _run_visitor(self, visitorcallable, element, **kwargs):
- raise NotImplementedError()
-
- def _execute_clauseelement(self, elem, multiparams=None, params=None):
+ """
raise NotImplementedError()
diff --git a/lib/sqlalchemy/engine/mock.py b/lib/sqlalchemy/engine/mock.py
index 803fe30a2..5da716b6b 100644
--- a/lib/sqlalchemy/engine/mock.py
+++ b/lib/sqlalchemy/engine/mock.py
@@ -10,7 +10,6 @@ from operator import attrgetter
from . import base
from . import url as _url
from .. import util
-from ..sql import ddl
class MockConnection(base.Connectable):
@@ -22,32 +21,15 @@ class MockConnection(base.Connectable):
dialect = property(attrgetter("_dialect"))
name = property(lambda s: s._dialect.name)
- def schema_for_object(self, obj):
- return obj.schema
-
def connect(self, **kwargs):
return self
+ def schema_for_object(self, obj):
+ return obj.schema
+
def execution_options(self, **kw):
return self
- def compiler(self, statement, parameters, **kwargs):
- return self._dialect.compiler(
- statement, parameters, engine=self, **kwargs
- )
-
- def create(self, entity, **kwargs):
- kwargs["checkfirst"] = False
-
- ddl.SchemaGenerator(self.dialect, self, **kwargs).traverse_single(
- entity
- )
-
- def drop(self, entity, **kwargs):
- kwargs["checkfirst"] = False
-
- ddl.SchemaDropper(self.dialect, self, **kwargs).traverse_single(entity)
-
def _run_ddl_visitor(
self, visitorcallable, element, connection=None, **kwargs
):
diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py
index 3c2e682be..48572c7fe 100644
--- a/lib/sqlalchemy/engine/result.py
+++ b/lib/sqlalchemy/engine/result.py
@@ -25,8 +25,6 @@ from ..util import py2k
if _baserow_usecext:
from sqlalchemy.cresultproxy import tuplegetter
-
- _row_as_tuple = tuplegetter
else:
def tuplegetter(*indexes):
@@ -37,16 +35,6 @@ else:
else:
return lambda row: (it(row),)
- def _row_as_tuple(*indexes):
- # circumvent LegacyRow.__getitem__ pointing to
- # _get_by_key_impl_mapping for now. otherwise we could
- # use itemgetter
- getters = [
- operator.methodcaller("_get_by_int_impl", index)
- for index in indexes
- ]
- return lambda rec: tuple([getter(rec) for getter in getters])
-
class ResultMetaData(object):
"""Base for metadata about result rows."""
@@ -71,13 +59,6 @@ class ResultMetaData(object):
assert raiseerr
util.raise_(KeyError(key), replace_context=err)
- def _warn_for_nonint(self, key):
- util.warn_deprecated_20(
- "Retrieving row members using strings or other non-integers is "
- "deprecated; use row._mapping for a dictionary interface "
- "to the row"
- )
-
def _raise_for_nonint(self, key):
raise TypeError(
"TypeError: tuple indices must be integers or slices, not %s"
@@ -104,7 +85,7 @@ class ResultMetaData(object):
def _row_as_tuple_getter(self, keys):
indexes = self._indexes_for_keys(keys)
- return _row_as_tuple(*indexes)
+ return tuplegetter(*indexes)
class RMKeyView(collections_abc.KeysView):
diff --git a/lib/sqlalchemy/engine/row.py b/lib/sqlalchemy/engine/row.py
index dc11e3548..e268cfec9 100644
--- a/lib/sqlalchemy/engine/row.py
+++ b/lib/sqlalchemy/engine/row.py
@@ -40,19 +40,12 @@ except ImportError:
KEY_INTEGER_ONLY = 0
-"""__getitem__ only allows integer values, raises TypeError otherwise"""
+"""__getitem__ only allows integer values and slices, raises TypeError
+ otherwise"""
KEY_OBJECTS_ONLY = 1
"""__getitem__ only allows string/object values, raises TypeError otherwise"""
-KEY_OBJECTS_BUT_WARN = 2
-"""__getitem__ allows integer or string/object values, but emits a 2.0
-deprecation warning if string/object is passed"""
-
-KEY_OBJECTS_NO_WARN = 3
-"""__getitem__ allows integer or string/object values with no warnings
-or errors."""
-
try:
from sqlalchemy.cresultproxy import BaseRow
@@ -116,31 +109,12 @@ except ImportError:
if int in key.__class__.__mro__:
return self._data[key]
- if self._key_style == KEY_INTEGER_ONLY:
- self._parent._raise_for_nonint(key)
-
- # the following is all LegacyRow support. none of this
- # should be called if not LegacyRow
- # assert isinstance(self, LegacyRow)
-
- try:
- rec = self._keymap[key]
- except KeyError as ke:
- rec = self._parent._key_fallback(key, ke)
- except TypeError:
- if isinstance(key, slice):
- return tuple(self._data[key])
- else:
- raise
-
- mdindex = rec[MD_INDEX]
- if mdindex is None:
- self._parent._raise_for_ambiguous_column_name(rec)
+ assert self._key_style == KEY_INTEGER_ONLY
- elif self._key_style == KEY_OBJECTS_BUT_WARN and mdindex != key:
- self._parent._warn_for_nonint(key)
+ if isinstance(key, slice):
+ return tuple(self._data[key])
- return self._data[mdindex]
+ self._parent._raise_for_nonint(key)
# The original 1.4 plan was that Row would not allow row["str"]
# access, however as the C extensions were inadvertently allowing
@@ -190,26 +164,19 @@ class Row(BaseRow, collections_abc.Sequence):
:ref:`coretutorial_selecting` - includes examples of selecting
rows from SELECT statements.
- :class:`.LegacyRow` - Compatibility interface introduced in SQLAlchemy
- 1.4.
-
.. versionchanged:: 1.4
- Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a
+ Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a
"proxy" object in that it contains the final form of data within it,
- and now acts mostly like a named tuple. Mapping-like functionality is
- moved to the :attr:`.Row._mapping` attribute, but will remain available
- in SQLAlchemy 1.x series via the :class:`.LegacyRow` class that is used
- by :class:`_engine.LegacyCursorResult`.
- See :ref:`change_4710_core` for background
- on this change.
+ and now acts mostly like a named tuple. Mapping-like functionality is
+ moved to the :attr:`.Row._mapping` attribute. See
+ :ref:`change_4710_core` for background on this change.
"""
__slots__ = ()
- # in 2.0, this should be KEY_INTEGER_ONLY
- _default_key_style = KEY_OBJECTS_BUT_WARN
+ _default_key_style = KEY_INTEGER_ONLY
@property
def _mapping(self):
@@ -217,10 +184,7 @@ class Row(BaseRow, collections_abc.Sequence):
This object provides a consistent Python mapping (i.e. dictionary)
interface for the data contained within the row. The :class:`.Row`
- by itself behaves like a named tuple, however in the 1.4 series of
- SQLAlchemy, the :class:`.LegacyRow` class is still used by Core which
- continues to have mapping-like behaviors against the row object
- itself.
+ by itself behaves like a named tuple.
.. seealso::
@@ -304,32 +268,6 @@ class Row(BaseRow, collections_abc.Sequence):
def __repr__(self):
return repr(sql_util._repr_row(self))
- @util.deprecated_20(
- ":meth:`.Row.keys`",
- alternative="Use the namedtuple standard accessor "
- ":attr:`.Row._fields`, or for full mapping behavior use "
- "row._mapping.keys() ",
- )
- def keys(self):
- """Return the list of keys as strings represented by this
- :class:`.Row`.
-
- The keys can represent the labels of the columns returned by a core
- statement or the names of the orm classes returned by an orm
- execution.
-
- This method is analogous to the Python dictionary ``.keys()`` method,
- except that it returns a list, not an iterator.
-
- .. seealso::
-
- :attr:`.Row._fields`
-
- :attr:`.Row._mapping`
-
- """
- return self._parent.keys
-
@property
def _fields(self):
"""Return a tuple of string keys as represented by this
@@ -376,130 +314,6 @@ class Row(BaseRow, collections_abc.Sequence):
raise NotImplementedError()
-class LegacyRow(Row):
- """A subclass of :class:`.Row` that delivers 1.x SQLAlchemy behaviors
- for Core.
-
- The :class:`.LegacyRow` class is where most of the Python mapping
- (i.e. dictionary-like)
- behaviors are implemented for the row object. The mapping behavior
- of :class:`.Row` going forward is accessible via the :class:`.Row._mapping`
- attribute.
-
- .. versionadded:: 1.4 - added :class:`.LegacyRow` which encapsulates most
- of the deprecated behaviors of :class:`.Row`.
-
- """
-
- __slots__ = ()
-
- if util.SQLALCHEMY_WARN_20:
- _default_key_style = KEY_OBJECTS_BUT_WARN
- else:
- _default_key_style = KEY_OBJECTS_NO_WARN
-
- def __contains__(self, key):
- return self._parent._contains(key, self)
-
- # prior to #6218, LegacyRow would redirect the behavior of __getitem__
- # for the non C version of BaseRow. This is now set up by Python BaseRow
- # in all cases
- # if not _baserow_usecext:
- # __getitem__ = BaseRow._get_by_key_impl
-
- @util.deprecated(
- "1.4",
- "The :meth:`.LegacyRow.has_key` method is deprecated and will be "
- "removed in a future release. To test for key membership, use "
- "the :attr:`Row._mapping` attribute, i.e. 'key in row._mapping`.",
- )
- def has_key(self, key):
- """Return True if this :class:`.LegacyRow` contains the given key.
-
- Through the SQLAlchemy 1.x series, the ``__contains__()`` method of
- :class:`.Row` (or :class:`.LegacyRow` as of SQLAlchemy 1.4) also links
- to :meth:`.Row.has_key`, in that an expression such as ::
-
- "some_col" in row
-
- Will return True if the row contains a column named ``"some_col"``,
- in the way that a Python mapping works.
-
- However, it is planned that the 2.0 series of SQLAlchemy will reverse
- this behavior so that ``__contains__()`` will refer to a value being
- present in the row, in the way that a Python tuple works.
-
- .. seealso::
-
- :ref:`change_4710_core`
-
- """
-
- return self._parent._has_key(key)
-
- @util.deprecated(
- "1.4",
- "The :meth:`.LegacyRow.items` method is deprecated and will be "
- "removed in a future release. Use the :attr:`Row._mapping` "
- "attribute, i.e., 'row._mapping.items()'.",
- )
- def items(self):
- """Return a list of tuples, each tuple containing a key/value pair.
-
- This method is analogous to the Python dictionary ``.items()`` method,
- except that it returns a list, not an iterator.
-
- """
-
- return [(key, self[key]) for key in self.keys()]
-
- @util.deprecated(
- "1.4",
- "The :meth:`.LegacyRow.iterkeys` method is deprecated and will be "
- "removed in a future release. Use the :attr:`Row._mapping` "
- "attribute, i.e., 'row._mapping.keys()'.",
- )
- def iterkeys(self):
- """Return a an iterator against the :meth:`.Row.keys` method.
-
- This method is analogous to the Python-2-only dictionary
- ``.iterkeys()`` method.
-
- """
- return iter(self._parent.keys)
-
- @util.deprecated(
- "1.4",
- "The :meth:`.LegacyRow.itervalues` method is deprecated and will be "
- "removed in a future release. Use the :attr:`Row._mapping` "
- "attribute, i.e., 'row._mapping.values()'.",
- )
- def itervalues(self):
- """Return a an iterator against the :meth:`.Row.values` method.
-
- This method is analogous to the Python-2-only dictionary
- ``.itervalues()`` method.
-
- """
- return iter(self)
-
- @util.deprecated(
- "1.4",
- "The :meth:`.LegacyRow.values` method is deprecated and will be "
- "removed in a future release. Use the :attr:`Row._mapping` "
- "attribute, i.e., 'row._mapping.values()'.",
- )
- def values(self):
- """Return the values represented by this :class:`.Row` as a list.
-
- This method is analogous to the Python dictionary ``.values()`` method,
- except that it returns a list, not an iterator.
-
- """
-
- return self._values_impl()
-
-
BaseRowProxy = BaseRow
RowProxy = Row
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index aba80222a..4bcb655d6 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -937,46 +937,6 @@ class Executable(roles.StatementRole, Generative):
"""
return self._execution_options
- @util.deprecated_20(
- ":meth:`.Executable.execute`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`.",
- )
- def execute(self, *multiparams, **params):
- """Compile and execute this :class:`.Executable`."""
- e = self.bind
- if e is None:
- label = (
- getattr(self, "description", None) or self.__class__.__name__
- )
- msg = (
- "This %s is not directly bound to a Connection or Engine. "
- "Use the .execute() method of a Connection or Engine "
- "to execute this construct." % label
- )
- raise exc.UnboundExecutionError(msg)
- return e._execute_clauseelement(
- self, multiparams, params, util.immutabledict()
- )
-
- @util.deprecated_20(
- ":meth:`.Executable.scalar`",
- alternative="Scalar execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.scalar` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.scalar` method of "
- ":class:`.Session`.",
- )
- def scalar(self, *multiparams, **params):
- """Compile and execute this :class:`.Executable`, returning the
- result's scalar representation.
-
- """
- return self.execute(*multiparams, **params).scalar()
-
@property
@util.deprecated_20(
":attr:`.Executable.bind`",
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 0cd568fcc..5c3fbb2b1 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -3186,6 +3186,8 @@ class SQLCompiler(Compiled):
# passed in. for ORM use this will convert from an ORM-state
# SELECT to a regular "Core" SELECT. other composed operations
# such as computation of joins will be performed.
+ kwargs["within_columns_clause"] = False
+
compile_state = select_stmt._compile_state_factory(
select_stmt, self, **kwargs
)
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index 166ad98cd..e45e22564 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -2517,19 +2517,6 @@ class DefaultGenerator(Executable, SchemaItem):
else:
self.column.default = self
- @util.deprecated_20(
- ":meth:`.DefaultGenerator.execute`",
- alternative="All statement execution in SQLAlchemy 2.0 is performed "
- "by the :meth:`_engine.Connection.execute` method of "
- ":class:`_engine.Connection`, "
- "or in the ORM by the :meth:`.Session.execute` method of "
- ":class:`.Session`.",
- )
- def execute(self, bind=None):
- if bind is None:
- bind = _bind_or_error(self)
- return bind._execute_default(self, (), util.EMPTY_DICT)
-
def _execute_on_connection(
self, connection, multiparams, params, execution_options
):
diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py
index 6bf14aecd..c30fdf823 100644
--- a/lib/sqlalchemy/testing/assertions.py
+++ b/lib/sqlalchemy/testing/assertions.py
@@ -204,11 +204,7 @@ def _expect_warnings(
with mock.patch("warnings.warn", our_warn), mock.patch(
"sqlalchemy.util.SQLALCHEMY_WARN_20", True
- ), mock.patch(
- "sqlalchemy.util.deprecations.SQLALCHEMY_WARN_20", True
- ), mock.patch(
- "sqlalchemy.engine.row.LegacyRow._default_key_style", 2
- ):
+ ), mock.patch("sqlalchemy.util.deprecations.SQLALCHEMY_WARN_20", True):
try:
yield
finally:
diff --git a/lib/sqlalchemy/testing/profiling.py b/lib/sqlalchemy/testing/profiling.py
index de4847f2f..dd5040205 100644
--- a/lib/sqlalchemy/testing/profiling.py
+++ b/lib/sqlalchemy/testing/profiling.py
@@ -243,17 +243,12 @@ def function_call_count(variance=0.05, times=1, warmup=0):
from sqlalchemy.util import decorator
from sqlalchemy.util import deprecations
- from sqlalchemy.engine import row
from sqlalchemy.testing import mock
@decorator
def wrap(fn, *args, **kw):
- with mock.patch.object(
- deprecations, "SQLALCHEMY_WARN_20", False
- ), mock.patch.object(
- row.LegacyRow, "_default_key_style", row.KEY_OBJECTS_NO_WARN
- ):
+ with mock.patch.object(deprecations, "SQLALCHEMY_WARN_20", False):
for warm in range(warmup):
fn(*args, **kw)
diff --git a/lib/sqlalchemy/testing/suite/test_select.py b/lib/sqlalchemy/testing/suite/test_select.py
index a3475f651..63502b077 100644
--- a/lib/sqlalchemy/testing/suite/test_select.py
+++ b/lib/sqlalchemy/testing/suite/test_select.py
@@ -206,8 +206,8 @@ class FetchLimitOffsetTest(fixtures.TablesTest):
eq_(connection.execute(select, params).fetchall(), result)
def _assert_result_str(self, select, result, params=()):
- conn = config.db.connect(close_with_result=True)
- eq_(conn.exec_driver_sql(select, params).fetchall(), result)
+ with config.db.connect() as conn:
+ eq_(conn.exec_driver_sql(select, params).fetchall(), result)
def test_simple_limit(self, connection):
table = self.tables.some_table