diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-09-18 13:29:42 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-09-18 22:35:48 -0400 |
commit | f0f08db5715e41cc08e57dbc76a85300bd68f8de (patch) | |
tree | 3d17072c461927f5a66c654d3e824a24b949905b /lib/sqlalchemy/sql/util.py | |
parent | d5c89a541f5233baf6b6a7498746820caa7b407f (diff) | |
download | sqlalchemy-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.py | 72 |
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 |