summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/util.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-09-18 13:29:42 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-09-18 22:35:48 -0400
commitf0f08db5715e41cc08e57dbc76a85300bd68f8de (patch)
tree3d17072c461927f5a66c654d3e824a24b949905b /lib/sqlalchemy/sql/util.py
parentd5c89a541f5233baf6b6a7498746820caa7b407f (diff)
downloadsqlalchemy-f0f08db5715e41cc08e57dbc76a85300bd68f8de.tar.gz
Complete deprecation of from_self()
For most from_self() tests, move them into test/orm/test_deprecated.py and replace the existing test with one that uses aliased() plus a subquery. This then revealed a few more issues. Related items: * Added slice() method to GenerativeSelect, to match that of orm.Query and to make possible migration of one of the from_self() tests. moved the utility functions used for this from orm/util into sql/util. * repairs a caching issue related to subqueryload where information being derived from the cached path info was mixing up with query information based on the per-query state, specifically an AliasedClass that is per query. * for the above issue, it seemed like path_registry maybe had to change so that it represents AliasedClass objects as their cache key rather than on identity, but it wasn't needed. still seems like it would be more correct. * enhances the error message raised by coercions for a case such as when an AliasedClass holds onto a select() object and not a subquery(); will name the original and resolved object for clarity (although how is AliasedClass able to accept a Select() object in the first place?) * Added _set_propagate_attrs() to Query so that again if it's passed to AliasedClass, it doesn't raise an error during coercion, but again maybe that should also be rejected up front Fixes: #5368 Change-Id: I5912aa611d899acc87a75eb5ee9f95990592f210
Diffstat (limited to 'lib/sqlalchemy/sql/util.py')
-rw-r--r--lib/sqlalchemy/sql/util.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index b3ead718a..264976cc8 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -12,7 +12,9 @@
from collections import deque
from itertools import chain
+from . import coercions
from . import operators
+from . import roles
from . import visitors
from .annotation import _deep_annotate # noqa
from .annotation import _deep_deannotate # noqa
@@ -980,3 +982,73 @@ class ColumnAdapter(ClauseAdapter):
def __setstate__(self, state):
self.__dict__.update(state)
self.columns = util.WeakPopulateDict(self._locate_col)
+
+
+def _offset_or_limit_clause(element, name=None, type_=None):
+ """Convert the given value to an "offset or limit" clause.
+
+ This handles incoming integers and converts to an expression; if
+ an expression is already given, it is passed through.
+
+ """
+ return coercions.expect(
+ roles.LimitOffsetRole, element, name=name, type_=type_
+ )
+
+
+def _offset_or_limit_clause_asint_if_possible(clause):
+ """Return the offset or limit clause as a simple integer if possible,
+ else return the clause.
+
+ """
+ if clause is None:
+ return None
+ if hasattr(clause, "_limit_offset_value"):
+ value = clause._limit_offset_value
+ return util.asint(value)
+ else:
+ return clause
+
+
+def _make_slice(limit_clause, offset_clause, start, stop):
+ """Compute LIMIT/OFFSET in terms of slice start/end
+ """
+
+ # for calculated limit/offset, try to do the addition of
+ # values to offset in Python, however if a SQL clause is present
+ # then the addition has to be on the SQL side.
+ if start is not None and stop is not None:
+ offset_clause = _offset_or_limit_clause_asint_if_possible(
+ offset_clause
+ )
+ if offset_clause is None:
+ offset_clause = 0
+
+ if start != 0:
+ offset_clause = offset_clause + start
+
+ if offset_clause == 0:
+ offset_clause = None
+ else:
+ offset_clause = _offset_or_limit_clause(offset_clause)
+
+ limit_clause = _offset_or_limit_clause(stop - start)
+
+ elif start is None and stop is not None:
+ limit_clause = _offset_or_limit_clause(stop)
+ elif start is not None and stop is None:
+ offset_clause = _offset_or_limit_clause_asint_if_possible(
+ offset_clause
+ )
+ if offset_clause is None:
+ offset_clause = 0
+
+ if start != 0:
+ offset_clause = offset_clause + start
+
+ if offset_clause == 0:
+ offset_clause = None
+ else:
+ offset_clause = _offset_or_limit_clause(offset_clause)
+
+ return limit_clause, offset_clause