diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-08-15 18:12:42 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-08-17 14:30:24 -0400 |
commit | 09df554a85ff1f9e35e4275465ba4eca029a61b3 (patch) | |
tree | 8286ebf4bb54e103e10a0be1ebd3cab2152e16b4 /lib/sqlalchemy/sql/lambdas.py | |
parent | 76b506ed51e31b922014a30de2a5952d1a6ad891 (diff) | |
download | sqlalchemy-09df554a85ff1f9e35e4275465ba4eca029a61b3.tar.gz |
honor NO_CACHE in lambdas
Fixed issue in lambda caching system where an element of a query that
produces no cache key, like a custom option or clause element, would still
populate the expression in the "lambda cache" inappropriately.
This was discovered as part of :ticket:`6887` but is a separate
issue.
References: #6887
Change-Id: I1665f4320254ddc63a0abf3088e9daeaffbd1840
Diffstat (limited to 'lib/sqlalchemy/sql/lambdas.py')
-rw-r--r-- | lib/sqlalchemy/sql/lambdas.py | 99 |
1 files changed, 70 insertions, 29 deletions
diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index d33e8ebfb..36e470ce7 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -182,28 +182,49 @@ class LambdaElement(elements.ClauseElement): self._resolved_bindparams = bindparams = [] - anon_map = traversals.anon_map() - cache_key = tuple( - [ - getter(closure, opts, anon_map, bindparams) - for getter in tracker.closure_trackers - ] - ) - if self.parent_lambda is not None: - cache_key = self.parent_lambda.closure_cache_key + cache_key + parent_closure_cache_key = self.parent_lambda.closure_cache_key + else: + parent_closure_cache_key = () + + if parent_closure_cache_key is not traversals.NO_CACHE: + anon_map = traversals.anon_map() + cache_key = tuple( + [ + getter(closure, opts, anon_map, bindparams) + for getter in tracker.closure_trackers + ] + ) - self.closure_cache_key = cache_key + if traversals.NO_CACHE not in anon_map: + cache_key = parent_closure_cache_key + cache_key - try: - rec = lambda_cache[tracker_key + cache_key] - except KeyError: + self.closure_cache_key = cache_key + + try: + rec = lambda_cache[tracker_key + cache_key] + except KeyError: + rec = None + else: + cache_key = traversals.NO_CACHE + rec = None + + else: + cache_key = traversals.NO_CACHE rec = None + self.closure_cache_key = cache_key + if rec is None: - rec = AnalyzedFunction(tracker, self, apply_propagate_attrs, fn) - rec.closure_bindparams = bindparams - lambda_cache[tracker_key + cache_key] = rec + if cache_key is not traversals.NO_CACHE: + rec = AnalyzedFunction( + tracker, self, apply_propagate_attrs, fn + ) + rec.closure_bindparams = bindparams + lambda_cache[tracker_key + cache_key] = rec + else: + rec = NonAnalyzedFunction(self._invoke_user_fn(fn)) + else: bindparams[:] = [ orig_bind._with_value(new_bind.value, maintain_key=True) @@ -212,21 +233,24 @@ class LambdaElement(elements.ClauseElement): ) ] - if self.parent_lambda is not None: - bindparams[:0] = self.parent_lambda._resolved_bindparams - self._rec = rec - lambda_element = self - while lambda_element is not None: - rec = lambda_element._rec - if rec.bindparam_trackers: - tracker_instrumented_fn = rec.tracker_instrumented_fn - for tracker in rec.bindparam_trackers: - tracker( - lambda_element.fn, tracker_instrumented_fn, bindparams - ) - lambda_element = lambda_element.parent_lambda + if cache_key is not traversals.NO_CACHE: + if self.parent_lambda is not None: + bindparams[:0] = self.parent_lambda._resolved_bindparams + + lambda_element = self + while lambda_element is not None: + rec = lambda_element._rec + if rec.bindparam_trackers: + tracker_instrumented_fn = rec.tracker_instrumented_fn + for tracker in rec.bindparam_trackers: + tracker( + lambda_element.fn, + tracker_instrumented_fn, + bindparams, + ) + lambda_element = lambda_element.parent_lambda return rec @@ -304,6 +328,9 @@ class LambdaElement(elements.ClauseElement): return expr def _gen_cache_key(self, anon_map, bindparams): + if self.closure_cache_key is traversals.NO_CACHE: + anon_map[traversals.NO_CACHE] = True + return None cache_key = ( self.fn.__code__, @@ -914,6 +941,20 @@ class AnalyzedCode(object): ) +class NonAnalyzedFunction(object): + __slots__ = ("expr",) + + closure_bindparams = None + bindparam_trackers = None + + def __init__(self, expr): + self.expr = expr + + @property + def expected_expr(self): + return self.expr + + class AnalyzedFunction(object): __slots__ = ( "analyzed_code", |