summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-09-22 10:26:35 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-09-22 14:26:18 -0400
commit96888aee7611c15532af2ae47e567f68c28b2474 (patch)
tree88fa6819b09463a2b3b652c48ea9c3e370ba9443 /lib/sqlalchemy
parente973fd5d5712b7212624b329015633fecfd1a25c (diff)
downloadsqlalchemy-96888aee7611c15532af2ae47e567f68c28b2474.tar.gz
Deprecate negative slice indexes
The "slice index" feature used by :class:`_orm.Query` as well as by the dynamic relationship loader will no longer accept negative indexes in SQLAlchemy 2.0. These operations do not work efficiently and load the entire collection in, which is both surprising and undesirable. These will warn in 1.4 unless the :paramref:`_orm.Session.future` flag is set in which case they will raise IndexError. Fixes: #5606 Change-Id: I5f5dcf984a8f41ab3d0e233ef7553e77fd99a771
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/dynamic.py4
-rw-r--r--lib/sqlalchemy/orm/query.py6
-rw-r--r--lib/sqlalchemy/orm/util.py20
3 files changed, 25 insertions, 5 deletions
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index af67b4772..48161a256 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -510,7 +510,9 @@ class AppenderQuery(Generative):
attributes.PASSIVE_NO_INITIALIZE,
).indexed(index)
else:
- return orm_util._getitem(self, index)
+ return orm_util._getitem(
+ self, index, allow_negative=not self.session.future
+ )
@_generative
def limit(self, limit):
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index c6e6f6466..42987932d 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -2495,7 +2495,11 @@ class Query(
self._compile_options += {"_enable_single_crit": False}
def __getitem__(self, item):
- return orm_util._getitem(self, item)
+ return orm_util._getitem(
+ self,
+ item,
+ allow_negative=not self.session or not self.session.future,
+ )
@_generative
@_assertions(_no_statement_condition)
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index 27b14d95b..c8d639e8c 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -1811,12 +1811,26 @@ def randomize_unitofwork():
) = session.set = mapper.set = dependency.set = RandomSet
-def _getitem(iterable_query, item):
+def _getitem(iterable_query, item, allow_negative):
"""calculate __getitem__ in terms of an iterable query object
that also has a slice() method.
"""
+ def _no_negative_indexes():
+ if not allow_negative:
+ raise IndexError(
+ "negative indexes are not accepted by SQL "
+ "index / slice operators"
+ )
+ else:
+ util.warn_deprecated_20(
+ "Support for negative indexes for SQL index / slice operators "
+ "will be "
+ "removed in 2.0; these operators fetch the complete result "
+ "and do not work efficiently."
+ )
+
if isinstance(item, slice):
start, stop, step = util.decode_slice(item)
@@ -1827,11 +1841,10 @@ def _getitem(iterable_query, item):
):
return []
- # perhaps we should execute a count() here so that we
- # can still use LIMIT/OFFSET ?
elif (isinstance(start, int) and start < 0) or (
isinstance(stop, int) and stop < 0
):
+ _no_negative_indexes()
return list(iterable_query)[item]
res = iterable_query.slice(start, stop)
@@ -1841,6 +1854,7 @@ def _getitem(iterable_query, item):
return list(res)
else:
if item == -1:
+ _no_negative_indexes()
return list(iterable_query)[-1]
else:
return list(iterable_query[item : item + 1])[0]