diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/orm/relationships.py | 39 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/annotation.py | 12 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/util.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/util/_concurrency_py3k.py | 4 |
4 files changed, 52 insertions, 4 deletions
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 8d9f3c644..e5a6b9afa 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -80,6 +80,7 @@ from ..sql._typing import _ColumnExpressionArgument from ..sql._typing import _HasClauseElement from ..sql.elements import ColumnClause from ..sql.elements import ColumnElement +from ..sql.util import _deep_annotate from ..sql.util import _deep_deannotate from ..sql.util import _shallow_annotate from ..sql.util import adapt_criterion_to_null @@ -115,6 +116,7 @@ if typing.TYPE_CHECKING: from ..sql._typing import _EquivalentColumnMap from ..sql._typing import _InfoType from ..sql.annotation import _AnnotationDict + from ..sql.annotation import SupportsAnnotations from ..sql.elements import BinaryExpression from ..sql.elements import BindParameter from ..sql.elements import ClauseElement @@ -3284,6 +3286,38 @@ class JoinCondition: primaryjoin = primaryjoin & single_crit if extra_criteria: + + def mark_unrelated_columns_as_ok_to_adapt( + elem: SupportsAnnotations, annotations: _AnnotationDict + ) -> SupportsAnnotations: + """note unrelated columns in the "extra criteria" as OK + to adapt, even though they are not part of our "local" + or "remote" side. + + see #9779 for this case + + """ + + parentmapper_for_element = elem._annotations.get( + "parentmapper", None + ) + if ( + parentmapper_for_element is not self.prop.parent + and parentmapper_for_element is not self.prop.mapper + ): + return elem._annotate(annotations) + else: + return elem + + extra_criteria = tuple( + _deep_annotate( + elem, + {"ok_to_adapt_in_join_condition": True}, + annotate_callable=mark_unrelated_columns_as_ok_to_adapt, + ) + for elem in extra_criteria + ) + if secondaryjoin is not None: secondaryjoin = secondaryjoin & sql.and_(*extra_criteria) else: @@ -3409,7 +3443,10 @@ class _ColInAnnotations: self.name = name def __call__(self, c: ClauseElement) -> bool: - return self.name in c._annotations + return ( + self.name in c._annotations + or "ok_to_adapt_in_join_condition" in c._annotations + ) class Relationship( # type: ignore diff --git a/lib/sqlalchemy/sql/annotation.py b/lib/sqlalchemy/sql/annotation.py index 7487e074c..e6dee7d17 100644 --- a/lib/sqlalchemy/sql/annotation.py +++ b/lib/sqlalchemy/sql/annotation.py @@ -406,8 +406,12 @@ def _deep_annotate( element: _SA, annotations: _AnnotationDict, exclude: Optional[Sequence[SupportsAnnotations]] = None, + *, detect_subquery_cols: bool = False, ind_cols_on_fromclause: bool = False, + annotate_callable: Optional[ + Callable[[SupportsAnnotations, _AnnotationDict], SupportsAnnotations] + ] = None, ) -> _SA: """Deep copy the given ClauseElement, annotating each element with the given annotations dictionary. @@ -446,9 +450,13 @@ def _deep_annotate( newelem = elem._clone(clone=clone, **kw) elif annotations != elem._annotations: if detect_subquery_cols and elem._is_immutable: - newelem = elem._clone(clone=clone, **kw)._annotate(annotations) + to_annotate = elem._clone(clone=clone, **kw) else: - newelem = elem._annotate(annotations) + to_annotate = elem + if annotate_callable: + newelem = annotate_callable(to_annotate, annotations) + else: + newelem = to_annotate._annotate(annotations) else: newelem = elem diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 0a50197a0..18caf5de4 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -1343,6 +1343,7 @@ class ColumnAdapter(ClauseAdapter): def traverse( self, obj: Optional[ExternallyTraversible] ) -> Optional[ExternallyTraversible]: + return self.columns[obj] def chain(self, visitor: ExternalTraversal) -> ColumnAdapter: diff --git a/lib/sqlalchemy/util/_concurrency_py3k.py b/lib/sqlalchemy/util/_concurrency_py3k.py index 0e26425b2..3544a0fd5 100644 --- a/lib/sqlalchemy/util/_concurrency_py3k.py +++ b/lib/sqlalchemy/util/_concurrency_py3k.py @@ -258,4 +258,6 @@ def get_event_loop() -> asyncio.AbstractEventLoop: try: return asyncio.get_running_loop() except RuntimeError: - return asyncio.get_event_loop_policy().get_event_loop() + # avoid "During handling of the above exception, another exception..." + pass + return asyncio.get_event_loop_policy().get_event_loop() |