From 736f93473d351cf3ab7680e4ed373e19f997b3bb Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 15 Dec 2021 15:00:28 -0500 Subject: use load_scalar_attributes() for undefer At some point, undeferral of attributes started emitting a full ORM query for the entity, including that subclass columns would use a JOIN. Seems to be at least in 1.3 / 1.4 and possibly earlier. This JOINs to the superclass table unnecessarily. Use load_scalar_attributes() here which should handle the whole thing and emits a more efficient query for joined inheritance. As this behavior seems to have been throughout 1.3 and 1.4 at least, targeting at 2.0 is likely best. Fixes: #7463 Change-Id: Ie4bae767747bba0d03fb13eaff579d4bab0b1bc2 --- lib/sqlalchemy/orm/loading.py | 18 +++--------------- lib/sqlalchemy/orm/strategies.py | 17 ++++------------- 2 files changed, 7 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index d6ee9b7a7..a37d83cfe 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -16,7 +16,6 @@ as well as some of the attribute loading strategies. from . import attributes from . import exc as orm_exc from . import path_registry -from . import strategy_options from .base import _DEFER_FOR_STATE from .base import _RAISE_FOR_STATE from .base import _SET_DEFERRED_EXPIRED @@ -1386,25 +1385,14 @@ def load_scalar_attributes(mapper, state, attribute_names, passive): attribute_names = attribute_names.intersection(mapper.attrs.keys()) if mapper.inherits and not mapper.concrete: - # because we are using Core to produce a select() that we - # pass to the Query, we aren't calling setup() for mapped - # attributes; in 1.0 this means deferred attrs won't get loaded - # by default statement = mapper._optimized_get_statement(state, attribute_names) if statement is not None: - # this was previously aliased(mapper, statement), however, - # statement is a select() and Query's coercion now raises for this - # since you can't "select" from a "SELECT" statement. only - # from_statement() allows this. - # note: using from_statement() here means there is an adaption - # with adapt_on_names set up. the other option is to make the - # aliased() against a subquery which affects the SQL. from .query import FromStatement - stmt = FromStatement(mapper, statement).options( - strategy_options.Load(mapper).undefer("*") - ) + # undefer() isn't needed here because statement has the + # columns needed already, this implicitly undefers that column + stmt = FromStatement(mapper, statement) result = load_on_ident( session, diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index ff4ac9d33..51e81e104 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -24,6 +24,7 @@ from . import util as orm_util from .base import _DEFER_FOR_STATE from .base import _RAISE_FOR_STATE from .base import _SET_DEFERRED_EXPIRED +from .base import PASSIVE_OFF from .context import _column_descriptions from .context import ORMCompileState from .context import QueryContext @@ -512,19 +513,9 @@ class DeferredColumnLoader(LoaderStrategy): if self.raiseload: self._invoke_raise_load(state, passive, "raise") - if ( - loading.load_on_ident( - session, - sql.select(localparent).set_label_style( - LABEL_STYLE_TABLENAME_PLUS_COL - ), - state.key, - only_load_props=group, - refresh_state=state, - ) - is None - ): - raise orm_exc.ObjectDeletedError(state) + loading.load_scalar_attributes( + state.mapper, state, set(group), PASSIVE_OFF + ) return attributes.ATTR_WAS_SET -- cgit v1.2.1