summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/strategies.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-08-17 15:03:57 -0400
committermike bayer <mike_mp@zzzcomputing.com>2021-08-17 19:15:30 +0000
commit29485026e9ea6500ea451b1d4a6f66795a02428d (patch)
tree7fc54d5815fbe77158ad99868081a530111aa1a7 /lib/sqlalchemy/orm/strategies.py
parenta7899c44ba15076df8869f5b40d720ccf09d5273 (diff)
downloadsqlalchemy-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.py36
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)