--- /dev/null
+.. change::
+ :tags: bug, orm, regression
+ :tickets: 10098
+
+ Fixed additional regression caused by :ticket:`9805` where more aggressive
+ propagation of the "ORM" flag on statements could lead to an internal
+ attribute error when embedding an ORM :class:`.Query` construct that
+ nonetheless contained no ORM entities within a Core SQL statement, in this
+ case ORM-enabled UPDATE and DELETE statements.
+
except KeyError:
assert False, "statement had 'orm' plugin but no plugin_subject"
else:
- bind_arguments["mapper"] = plugin_subject.mapper
-
- update_options += {"_subject_mapper": plugin_subject.mapper}
+ if plugin_subject:
+ bind_arguments["mapper"] = plugin_subject.mapper
+ update_options += {"_subject_mapper": plugin_subject.mapper}
if "parententity" not in statement.table._annotations:
update_options += {"_dml_strategy": "core_only"}
except KeyError:
assert False, "statement had 'orm' plugin but no plugin_subject"
else:
- bind_arguments["mapper"] = plugin_subject.mapper
-
- insert_options += {"_subject_mapper": plugin_subject.mapper}
+ if plugin_subject:
+ bind_arguments["mapper"] = plugin_subject.mapper
+ insert_options += {"_subject_mapper": plugin_subject.mapper}
if not params:
if insert_options._dml_strategy == "auto":
from sqlalchemy import insert
from sqlalchemy import inspect
from sqlalchemy import Integer
+from sqlalchemy import literal
from sqlalchemy import select
from sqlalchemy import Sequence
from sqlalchemy import String
is_true(inspect(u1).detached)
is_(inspect(u1).session, None)
+ @testing.variation("construct", ["select", "update", "delete", "insert"])
+ def test_core_sql_w_embedded_orm(self, construct: testing.Variation):
+ """test #10098"""
+ user_table = self.tables.users
+
+ sess = fixture_session()
+
+ subq_1 = (
+ sess.query(user_table.c.id).where(
+ user_table.c.id == 10
+ ) # note user 10 exists but has no addresses, so
+ # this is significant for the test here
+ .scalar_subquery()
+ )
+
+ if construct.update:
+ stmt = (
+ user_table.update()
+ .values(name="xyz")
+ .where(user_table.c.id.in_(subq_1))
+ )
+ elif construct.delete:
+ stmt = user_table.delete().where(user_table.c.id.in_(subq_1))
+ elif construct.select:
+ stmt = select(user_table).where(user_table.c.id.in_(subq_1))
+ elif construct.insert:
+ stmt = insert(user_table).from_select(
+ ["id", "name"], select(subq_1 + 10, literal("xyz"))
+ )
+
+ # force the expected condition for INSERT; can't really get
+ # this to happen "naturally"
+ stmt._propagate_attrs = stmt._propagate_attrs.union(
+ {"compile_state_plugin": "orm", "plugin_subject": None}
+ )
+
+ else:
+ construct.fail()
+
+ # assert that the pre-condition we are specifically testing is
+ # present. if the implementation changes then this would have
+ # to change also.
+ eq_(
+ stmt._propagate_attrs,
+ {"compile_state_plugin": "orm", "plugin_subject": None},
+ )
+
+ result = sess.execute(stmt)
+
+ if construct.select:
+ result.all()
+
class FlushWarningsTest(fixtures.MappedTest):
run_setup_mappers = "each"