diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-08-17 15:03:57 -0400 |
|---|---|---|
| committer | mike bayer <mike_mp@zzzcomputing.com> | 2021-08-17 19:15:30 +0000 |
| commit | 29485026e9ea6500ea451b1d4a6f66795a02428d (patch) | |
| tree | 7fc54d5815fbe77158ad99868081a530111aa1a7 /lib/sqlalchemy/orm/strategies.py | |
| parent | a7899c44ba15076df8869f5b40d720ccf09d5273 (diff) | |
| download | sqlalchemy-29485026e9ea6500ea451b1d4a6f66795a02428d.tar.gz | |
send user defined options from the current query
Revised the means by which the
:attr:`_orm.ORMExecuteState.user_defined_options` accessor receives
:class:`_orm.UserDefinedOption` and related option objects from the
context, with particular emphasis on the "selectinload" on the loader
strategy where this previously was not working; other strategies did not
have this problem. The objects that are associated with the current query
being executed, and not that of a query being cached, are now propagated
unconditionally. This essentially separates them out from the "loader
strategy" options which are explicitly associated with the compiled state
of a query and need to be used in relation to the cached query.
The effect of this fix is that a user-defined option, such as those used
by the dogpile.caching example as well as for other recipes such as
defining a "shard id" for the horizontal sharing extension, will be
correctly propagated to eager and lazy loaders regardless of whether
a cached query was ultimately invoked.
Fixes: #6887
Change-Id: Ieaae5b01c85de26ea732ebd625e6e5823a470492
Diffstat (limited to 'lib/sqlalchemy/orm/strategies.py')
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 36 |
1 files changed, 21 insertions, 15 deletions
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index bf60c803d..069e5e667 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1512,21 +1512,14 @@ class SubqueryLoader(PostLoader): loadopt, ): - if orig_query is context.query: - options = new_options = orig_query._with_options - else: - # There's currently no test that exercises the necessity of - # this step for subqueryload. Added in #6881, it is necessary for - # selectinload, but its necessity for subqueryload is still - # theoretical. - options = orig_query._with_options - - new_options = [ - orig_opt._adjust_for_extra_criteria(context) - if orig_opt._is_strategy_option - else orig_opt - for orig_opt in options - ] + # note that because the subqueryload object + # does not re-use the cached query, instead always making + # use of the current invoked query, while we have two queries + # here (orig and context.query), they are both non-cached + # queries and we can transfer the options as is without + # adjusting for new criteria. Some work on #6881 / #6889 + # brought this into question. + new_options = orig_query._with_options if loadopt and loadopt._extra_criteria: @@ -2933,9 +2926,12 @@ class SelectInLoader(PostLoader, util.MemoizedSlots): if orig_query is context.query: options = new_options = orig_query._with_options + user_defined_options = [] else: options = orig_query._with_options + # propagate compile state options from the original query, + # updating their "extra_criteria" as necessary. # note this will create a different cache key than # "orig" options if extra_criteria is present, because the copy # of extra_criteria will have different boundparam than that of @@ -2946,6 +2942,14 @@ class SelectInLoader(PostLoader, util.MemoizedSlots): if orig_opt._is_strategy_option else orig_opt for orig_opt in options + if orig_opt._is_compile_state or orig_opt._is_legacy_option + ] + + # propagate user defined options from the current query + user_defined_options = [ + opt + for opt in context.query._with_options + if not opt._is_compile_state and not opt._is_legacy_option ] if loadopt and loadopt._extra_criteria: @@ -2959,6 +2963,8 @@ class SelectInLoader(PostLoader, util.MemoizedSlots): q = q.options(*new_options)._update_compile_options( {"_current_path": effective_path} ) + if user_defined_options: + q = q.options(*user_defined_options) if context.populate_existing: q = q.execution_options(populate_existing=True) |
