mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Проверка 09.02.2025
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# orm/strategies.py
|
||||
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -16,8 +16,10 @@ import collections
|
||||
import itertools
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
from . import attributes
|
||||
from . import exc as orm_exc
|
||||
@@ -45,7 +47,7 @@ from .interfaces import StrategizedProperty
|
||||
from .session import _state_session
|
||||
from .state import InstanceState
|
||||
from .strategy_options import Load
|
||||
from .util import _none_set
|
||||
from .util import _none_only_set
|
||||
from .util import AliasedClass
|
||||
from .. import event
|
||||
from .. import exc as sa_exc
|
||||
@@ -57,8 +59,10 @@ from ..sql import util as sql_util
|
||||
from ..sql import visitors
|
||||
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
|
||||
from ..sql.selectable import Select
|
||||
from ..util.typing import Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .mapper import Mapper
|
||||
from .relationships import RelationshipProperty
|
||||
from ..sql.elements import ColumnElement
|
||||
|
||||
@@ -932,8 +936,15 @@ class LazyLoader(
|
||||
elif LoaderCallableStatus.NEVER_SET in primary_key_identity:
|
||||
return LoaderCallableStatus.NEVER_SET
|
||||
|
||||
if _none_set.issuperset(primary_key_identity):
|
||||
return None
|
||||
# test for None alone in primary_key_identity based on
|
||||
# allow_partial_pks preference. PASSIVE_NO_RESULT and NEVER_SET
|
||||
# have already been tested above
|
||||
if not self.mapper.allow_partial_pks:
|
||||
if _none_only_set.intersection(primary_key_identity):
|
||||
return None
|
||||
else:
|
||||
if _none_only_set.issuperset(primary_key_identity):
|
||||
return None
|
||||
|
||||
if (
|
||||
self.key in state.dict
|
||||
@@ -1373,12 +1384,16 @@ class ImmediateLoader(PostLoader):
|
||||
adapter,
|
||||
populators,
|
||||
):
|
||||
if not context.compile_state.compile_options._enable_eagerloads:
|
||||
return
|
||||
|
||||
(
|
||||
effective_path,
|
||||
run_loader,
|
||||
execution_options,
|
||||
recursion_depth,
|
||||
) = self._setup_for_recursion(context, path, loadopt, self.join_depth)
|
||||
|
||||
if not run_loader:
|
||||
# this will not emit SQL and will only emit for a many-to-one
|
||||
# "use get" load. the "_RELATED" part means it may return
|
||||
@@ -1957,6 +1972,18 @@ class SubqueryLoader(PostLoader):
|
||||
adapter,
|
||||
populators,
|
||||
):
|
||||
if (
|
||||
loadopt
|
||||
and context.compile_state.statement is not None
|
||||
and context.compile_state.statement.is_dml
|
||||
):
|
||||
util.warn_deprecated(
|
||||
"The subqueryload loader option is not compatible with DML "
|
||||
"statements such as INSERT, UPDATE. Only SELECT may be used."
|
||||
"This warning will become an exception in a future release.",
|
||||
"2.0",
|
||||
)
|
||||
|
||||
if context.refresh_state:
|
||||
return self._immediateload_create_row_processor(
|
||||
context,
|
||||
@@ -2122,6 +2149,17 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
|
||||
if not compile_state.compile_options._enable_eagerloads:
|
||||
return
|
||||
elif (
|
||||
loadopt
|
||||
and compile_state.statement is not None
|
||||
and compile_state.statement.is_dml
|
||||
):
|
||||
util.warn_deprecated(
|
||||
"The joinedload loader option is not compatible with DML "
|
||||
"statements such as INSERT, UPDATE. Only SELECT may be used."
|
||||
"This warning will become an exception in a future release.",
|
||||
"2.0",
|
||||
)
|
||||
elif self.uselist:
|
||||
compile_state.multi_row_eager_loaders = True
|
||||
|
||||
@@ -2506,13 +2544,13 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
or query_entity.entity_zero.represents_outer_join
|
||||
or (chained_from_outerjoin and isinstance(towrap, sql.Join)),
|
||||
_left_memo=self.parent,
|
||||
_right_memo=self.mapper,
|
||||
_right_memo=path[self.mapper],
|
||||
_extra_criteria=extra_join_criteria,
|
||||
)
|
||||
else:
|
||||
# all other cases are innerjoin=='nested' approach
|
||||
eagerjoin = self._splice_nested_inner_join(
|
||||
path, towrap, clauses, onclause, extra_join_criteria
|
||||
path, path[-2], towrap, clauses, onclause, extra_join_criteria
|
||||
)
|
||||
|
||||
compile_state.eager_joins[query_entity_key] = eagerjoin
|
||||
@@ -2546,93 +2584,177 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
)
|
||||
|
||||
def _splice_nested_inner_join(
|
||||
self, path, join_obj, clauses, onclause, extra_criteria, splicing=False
|
||||
self,
|
||||
path,
|
||||
entity_we_want_to_splice_onto,
|
||||
join_obj,
|
||||
clauses,
|
||||
onclause,
|
||||
extra_criteria,
|
||||
entity_inside_join_structure: Union[
|
||||
Mapper, None, Literal[False]
|
||||
] = False,
|
||||
detected_existing_path: Optional[path_registry.PathRegistry] = None,
|
||||
):
|
||||
# recursive fn to splice a nested join into an existing one.
|
||||
# splicing=False means this is the outermost call, and it
|
||||
# should return a value. splicing=<from object> is the recursive
|
||||
# form, where it can return None to indicate the end of the recursion
|
||||
# entity_inside_join_structure=False means this is the outermost call,
|
||||
# and it should return a value. entity_inside_join_structure=<mapper>
|
||||
# indicates we've descended into a join and are looking at a FROM
|
||||
# clause representing this mapper; if this is not
|
||||
# entity_we_want_to_splice_onto then return None to end the recursive
|
||||
# branch
|
||||
|
||||
if splicing is False:
|
||||
# first call is always handed a join object
|
||||
# from the outside
|
||||
assert entity_we_want_to_splice_onto is path[-2]
|
||||
|
||||
if entity_inside_join_structure is False:
|
||||
assert isinstance(join_obj, orm_util._ORMJoin)
|
||||
elif isinstance(join_obj, sql.selectable.FromGrouping):
|
||||
|
||||
if isinstance(join_obj, sql.selectable.FromGrouping):
|
||||
# FromGrouping - continue descending into the structure
|
||||
return self._splice_nested_inner_join(
|
||||
path,
|
||||
entity_we_want_to_splice_onto,
|
||||
join_obj.element,
|
||||
clauses,
|
||||
onclause,
|
||||
extra_criteria,
|
||||
splicing,
|
||||
entity_inside_join_structure,
|
||||
)
|
||||
elif not isinstance(join_obj, orm_util._ORMJoin):
|
||||
if path[-2].isa(splicing):
|
||||
return orm_util._ORMJoin(
|
||||
join_obj,
|
||||
clauses.aliased_insp,
|
||||
onclause,
|
||||
isouter=False,
|
||||
_left_memo=splicing,
|
||||
_right_memo=path[-1].mapper,
|
||||
_extra_criteria=extra_criteria,
|
||||
)
|
||||
else:
|
||||
return None
|
||||
elif isinstance(join_obj, orm_util._ORMJoin):
|
||||
# _ORMJoin - continue descending into the structure
|
||||
|
||||
target_join = self._splice_nested_inner_join(
|
||||
path,
|
||||
join_obj.right,
|
||||
clauses,
|
||||
onclause,
|
||||
extra_criteria,
|
||||
join_obj._right_memo,
|
||||
)
|
||||
if target_join is None:
|
||||
right_splice = False
|
||||
join_right_path = join_obj._right_memo
|
||||
|
||||
# see if right side of join is viable
|
||||
target_join = self._splice_nested_inner_join(
|
||||
path,
|
||||
join_obj.left,
|
||||
entity_we_want_to_splice_onto,
|
||||
join_obj.right,
|
||||
clauses,
|
||||
onclause,
|
||||
extra_criteria,
|
||||
join_obj._left_memo,
|
||||
entity_inside_join_structure=(
|
||||
join_right_path[-1].mapper
|
||||
if join_right_path is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
if target_join is None:
|
||||
# should only return None when recursively called,
|
||||
# e.g. splicing refers to a from obj
|
||||
assert (
|
||||
splicing is not False
|
||||
), "assertion failed attempting to produce joined eager loads"
|
||||
return None
|
||||
else:
|
||||
right_splice = True
|
||||
|
||||
if right_splice:
|
||||
# for a right splice, attempt to flatten out
|
||||
# a JOIN b JOIN c JOIN .. to avoid needless
|
||||
# parenthesis nesting
|
||||
if not join_obj.isouter and not target_join.isouter:
|
||||
eagerjoin = join_obj._splice_into_center(target_join)
|
||||
if target_join is not None:
|
||||
# for a right splice, attempt to flatten out
|
||||
# a JOIN b JOIN c JOIN .. to avoid needless
|
||||
# parenthesis nesting
|
||||
if not join_obj.isouter and not target_join.isouter:
|
||||
eagerjoin = join_obj._splice_into_center(target_join)
|
||||
else:
|
||||
eagerjoin = orm_util._ORMJoin(
|
||||
join_obj.left,
|
||||
target_join,
|
||||
join_obj.onclause,
|
||||
isouter=join_obj.isouter,
|
||||
_left_memo=join_obj._left_memo,
|
||||
)
|
||||
|
||||
eagerjoin._target_adapter = target_join._target_adapter
|
||||
return eagerjoin
|
||||
|
||||
else:
|
||||
eagerjoin = orm_util._ORMJoin(
|
||||
# see if left side of join is viable
|
||||
target_join = self._splice_nested_inner_join(
|
||||
path,
|
||||
entity_we_want_to_splice_onto,
|
||||
join_obj.left,
|
||||
target_join,
|
||||
join_obj.onclause,
|
||||
isouter=join_obj.isouter,
|
||||
_left_memo=join_obj._left_memo,
|
||||
clauses,
|
||||
onclause,
|
||||
extra_criteria,
|
||||
entity_inside_join_structure=join_obj._left_memo,
|
||||
detected_existing_path=join_right_path,
|
||||
)
|
||||
else:
|
||||
eagerjoin = orm_util._ORMJoin(
|
||||
target_join,
|
||||
join_obj.right,
|
||||
join_obj.onclause,
|
||||
isouter=join_obj.isouter,
|
||||
_right_memo=join_obj._right_memo,
|
||||
)
|
||||
|
||||
eagerjoin._target_adapter = target_join._target_adapter
|
||||
return eagerjoin
|
||||
if target_join is not None:
|
||||
eagerjoin = orm_util._ORMJoin(
|
||||
target_join,
|
||||
join_obj.right,
|
||||
join_obj.onclause,
|
||||
isouter=join_obj.isouter,
|
||||
_right_memo=join_obj._right_memo,
|
||||
)
|
||||
eagerjoin._target_adapter = target_join._target_adapter
|
||||
return eagerjoin
|
||||
|
||||
# neither side viable, return None, or fail if this was the top
|
||||
# most call
|
||||
if entity_inside_join_structure is False:
|
||||
assert (
|
||||
False
|
||||
), "assertion failed attempting to produce joined eager loads"
|
||||
return None
|
||||
|
||||
# reached an endpoint (e.g. a table that's mapped, or an alias of that
|
||||
# table). determine if we can use this endpoint to splice onto
|
||||
|
||||
# is this the entity we want to splice onto in the first place?
|
||||
if not entity_we_want_to_splice_onto.isa(entity_inside_join_structure):
|
||||
return None
|
||||
|
||||
# path check. if we know the path how this join endpoint got here,
|
||||
# lets look at our path we are satisfying and see if we're in the
|
||||
# wrong place. This is specifically for when our entity may
|
||||
# appear more than once in the path, issue #11449
|
||||
# updated in issue #11965.
|
||||
if detected_existing_path and len(detected_existing_path) > 2:
|
||||
# this assertion is currently based on how this call is made,
|
||||
# where given a join_obj, the call will have these parameters as
|
||||
# entity_inside_join_structure=join_obj._left_memo
|
||||
# and entity_inside_join_structure=join_obj._right_memo.mapper
|
||||
assert detected_existing_path[-3] is entity_inside_join_structure
|
||||
|
||||
# from that, see if the path we are targeting matches the
|
||||
# "existing" path of this join all the way up to the midpoint
|
||||
# of this join object (e.g. the relationship).
|
||||
# if not, then this is not our target
|
||||
#
|
||||
# a test condition where this test is false looks like:
|
||||
#
|
||||
# desired splice: Node->kind->Kind
|
||||
# path of desired splice: NodeGroup->nodes->Node->kind
|
||||
# path we've located: NodeGroup->nodes->Node->common_node->Node
|
||||
#
|
||||
# above, because we want to splice kind->Kind onto
|
||||
# NodeGroup->nodes->Node, this is not our path because it actually
|
||||
# goes more steps than we want into self-referential
|
||||
# ->common_node->Node
|
||||
#
|
||||
# a test condition where this test is true looks like:
|
||||
#
|
||||
# desired splice: B->c2s->C2
|
||||
# path of desired splice: A->bs->B->c2s
|
||||
# path we've located: A->bs->B->c1s->C1
|
||||
#
|
||||
# above, we want to splice c2s->C2 onto B, and the located path
|
||||
# shows that the join ends with B->c1s->C1. so we will
|
||||
# add another join onto that, which would create a "branch" that
|
||||
# we might represent in a pseudopath as:
|
||||
#
|
||||
# B->c1s->C1
|
||||
# ->c2s->C2
|
||||
#
|
||||
# i.e. A JOIN B ON <bs> JOIN C1 ON <c1s>
|
||||
# JOIN C2 ON <c2s>
|
||||
#
|
||||
|
||||
if detected_existing_path[0:-2] != path.path[0:-1]:
|
||||
return None
|
||||
|
||||
return orm_util._ORMJoin(
|
||||
join_obj,
|
||||
clauses.aliased_insp,
|
||||
onclause,
|
||||
isouter=False,
|
||||
_left_memo=entity_inside_join_structure,
|
||||
_right_memo=path[path[-1].mapper],
|
||||
_extra_criteria=extra_criteria,
|
||||
)
|
||||
|
||||
def _create_eager_adapter(self, context, result, adapter, path, loadopt):
|
||||
compile_state = context.compile_state
|
||||
@@ -2681,6 +2803,10 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
adapter,
|
||||
populators,
|
||||
):
|
||||
|
||||
if not context.compile_state.compile_options._enable_eagerloads:
|
||||
return
|
||||
|
||||
if not self.parent.class_manager[self.key].impl.supports_population:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"'%s' does not support object "
|
||||
@@ -2960,6 +3086,9 @@ class SelectInLoader(PostLoader, util.MemoizedSlots):
|
||||
if not run_loader:
|
||||
return
|
||||
|
||||
if not context.compile_state.compile_options._enable_eagerloads:
|
||||
return
|
||||
|
||||
if not self.parent.class_manager[self.key].impl.supports_population:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"'%s' does not support object "
|
||||
@@ -3117,7 +3246,7 @@ class SelectInLoader(PostLoader, util.MemoizedSlots):
|
||||
orig_query = context.compile_state.select_statement
|
||||
|
||||
# the actual statement that was requested is this one:
|
||||
# context_query = context.query
|
||||
# context_query = context.user_passed_query
|
||||
#
|
||||
# that's not the cached one, however. So while it is of the identical
|
||||
# structure, if it has entities like AliasedInsp, which we get from
|
||||
@@ -3141,11 +3270,11 @@ class SelectInLoader(PostLoader, util.MemoizedSlots):
|
||||
|
||||
effective_path = path[self.parent_property]
|
||||
|
||||
if orig_query is context.query:
|
||||
if orig_query is context.user_passed_query:
|
||||
new_options = orig_query._with_options
|
||||
else:
|
||||
cached_options = orig_query._with_options
|
||||
uncached_options = context.query._with_options
|
||||
uncached_options = context.user_passed_query._with_options
|
||||
|
||||
# propagate compile state options from the original query,
|
||||
# updating their "extra_criteria" as necessary.
|
||||
|
Reference in New Issue
Block a user