From: Mike Bayer Date: Thu, 14 Apr 2022 16:01:16 +0000 (-0400) Subject: Ensure ORMInsert sets up bind state X-Git-Tag: rel_2_0_0b1~349 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f96c12db923624204110e56ce730f5aafbb9463;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Ensure ORMInsert sets up bind state Fixed regression where the change in #7861, released in version 1.4.33, that brought the :class:`.Insert` construct to be partially recognized as an ORM-enabled statement did not properly transfer the correct mapper / mapped table state to the :class:`.Session`, causing the :meth:`.Session.get_bind` method to fail for a :class:`.Session` that was bound to engines and/or connections using the :paramref:`.Session.binds` parameter. Fixes: #7936 Change-Id: If19edef8e2dd68335465429eb3d2f0bfdade4a4c --- diff --git a/doc/build/changelog/unreleased_14/7936.rst b/doc/build/changelog/unreleased_14/7936.rst new file mode 100644 index 0000000000..bcad142b0b --- /dev/null +++ b/doc/build/changelog/unreleased_14/7936.rst @@ -0,0 +1,11 @@ +.. change:: + :tags: bug, orm, regression + :tickets: 7936 + + Fixed regression where the change in #7861, released in version 1.4.33, + that brought the :class:`.Insert` construct to be partially recognized as + an ORM-enabled statement did not properly transfer the correct mapper / + mapped table state to the :class:`.Session`, causing the + :meth:`.Session.get_bind` method to fail for a :class:`.Session` that was + bound to engines and/or connections using the :paramref:`.Session.binds` + parameter. diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py index 7298d3630e..3229453e7a 100644 --- a/lib/sqlalchemy/orm/persistence.py +++ b/lib/sqlalchemy/orm/persistence.py @@ -2209,6 +2209,14 @@ class ORMInsert(ORMDMLState, InsertDMLState): bind_arguments, is_reentrant_invoke, ): + bind_arguments["clause"] = statement + try: + plugin_subject = statement._propagate_attrs["plugin_subject"] + except KeyError: + assert False, "statement had 'orm' plugin but no plugin_subject" + else: + bind_arguments["mapper"] = plugin_subject.mapper + return ( statement, util.immutabledict(execution_options), diff --git a/test/orm/test_bind.py b/test/orm/test_bind.py index bf39ee44c5..a6480365d0 100644 --- a/test/orm/test_bind.py +++ b/test/orm/test_bind.py @@ -324,6 +324,21 @@ class BindIntegrationTest(_fixtures.FixtureTest): lambda User: {"clause": mock.ANY, "mapper": inspect(User)}, "e1", ), + ( + lambda User: update(User) + .values(name="not ed") + .where(User.name == "ed"), + lambda User: {"clause": mock.ANY, "mapper": inspect(User)}, + "e1", + ), + ( + lambda User: insert(User).values(name="not ed"), + lambda User: { + "clause": mock.ANY, + "mapper": inspect(User), + }, + "e1", + ), ) def test_bind_through_execute( self, statement, expected_get_bind_args, expected_engine_name diff --git a/test/orm/test_update_delete.py b/test/orm/test_update_delete.py index b2743024ab..427e49e5e6 100644 --- a/test/orm/test_update_delete.py +++ b/test/orm/test_update_delete.py @@ -96,6 +96,39 @@ class UpdateDeleteTest(fixtures.MappedTest): ) cls.mapper_registry.map_imperatively(Address, addresses) + @testing.combinations("table", "mapper", "both", argnames="bind_type") + @testing.combinations( + "update", "insert", "delete", argnames="statement_type" + ) + def test_get_bind_scenarios(self, connection, bind_type, statement_type): + """test for #7936""" + + User = self.classes.User + + if statement_type == "insert": + stmt = insert(User).values( + {User.id: 5, User.age: 25, User.name: "spongebob"} + ) + elif statement_type == "update": + stmt = ( + update(User) + .where(User.id == 2) + .values({User.name: "spongebob"}) + ) + elif statement_type == "delete": + stmt = delete(User) + + binds = {} + if bind_type == "both": + binds = {User: connection, User.__table__: connection} + elif bind_type == "mapper": + binds = {User: connection} + elif bind_type == "table": + binds = {User.__table__: connection} + + with Session(binds=binds) as sess: + sess.execute(stmt) + def test_illegal_eval(self): User = self.classes.User s = fixture_session()