From d519bca7f953a0520cda79504dbc019e74e87b28 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 17 Apr 2023 10:16:35 -0400 Subject: [PATCH] dont assume _compile_options are present Fixed bug where various ORM-specific getters such as :attr:`.ORMExecuteState.is_column_load`, :attr:`.ORMExecuteState.is_relationship_load`, :attr:`.ORMExecuteState.loader_strategy_path` etc. would throw an ``AttributeError`` if the SQL statement itself were a "compound select" such as a UNION. Fixes: #9634 Change-Id: Ia37df5d6f89d6534d69237dcab294bd849ece28b (cherry picked from commit 89608ccd3f5e5796d578e9a39201f7c5c45a61fe) --- doc/build/changelog/unreleased_14/9634.rst | 11 ++++++++ lib/sqlalchemy/orm/session.py | 5 +++- test/orm/test_events.py | 32 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 doc/build/changelog/unreleased_14/9634.rst diff --git a/doc/build/changelog/unreleased_14/9634.rst b/doc/build/changelog/unreleased_14/9634.rst new file mode 100644 index 0000000000..664e85716b --- /dev/null +++ b/doc/build/changelog/unreleased_14/9634.rst @@ -0,0 +1,11 @@ +.. change:: + :tags: bug, orm + :tickets: 9634 + :versions: 2.0.10 + + Fixed bug where various ORM-specific getters such as + :attr:`.ORMExecuteState.is_column_load`, + :attr:`.ORMExecuteState.is_relationship_load`, + :attr:`.ORMExecuteState.loader_strategy_path` etc. would throw an + ``AttributeError`` if the SQL statement itself were a "compound select" + such as a UNION. diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 753d1ec5f9..5a7a8bb211 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -347,7 +347,10 @@ class ORMExecuteState(util.MemoizedSlots): def _orm_compile_options(self): if not self.is_select: return None - opts = self.statement._compile_options + try: + opts = self.statement._compile_options + except AttributeError: + return None if opts.isinstance(context.ORMCompileState.default_compile_options): return opts else: diff --git a/test/orm/test_events.py b/test/orm/test_events.py index efb39bd2fd..052b9e0163 100644 --- a/test/orm/test_events.py +++ b/test/orm/test_events.py @@ -8,6 +8,7 @@ from sqlalchemy import literal_column from sqlalchemy import select from sqlalchemy import String from sqlalchemy import testing +from sqlalchemy import text from sqlalchemy import update from sqlalchemy.orm import attributes from sqlalchemy.orm import class_mapper @@ -292,6 +293,37 @@ class ORMExecuteTest(_RemoveListeners, _fixtures.FixtureTest): return canary + @testing.combinations( + (lambda: select(1), True), + (lambda User: select(User).union(select(User)), True), + (lambda: text("select * from users"), False), + ) + def test_non_orm_statements(self, stmt, is_select): + sess = Session(testing.db, future=True) + + canary = self._flag_fixture(sess) + + User, Address = self.classes("User", "Address") + stmt = testing.resolve_lambda(stmt, User=User) + sess.execute(stmt).all() + + eq_( + canary.mock_calls, + [ + call.options( + bind_mapper=None, + all_mappers=[], + is_select=is_select, + is_update=False, + is_delete=False, + is_orm_statement=False, + is_relationship_load=False, + is_column_load=False, + lazy_loaded_from=None, + ) + ], + ) + def test_all_mappers_accessor_one(self): User, Address = self.classes("User", "Address") -- 2.47.2