Fixed issues that prevented the new usage patterns for using DML with ORM
objects presented at :ref:`orm_dml_returning_objects` from working
correctly with the SQL Server pyodbc dialect.
Here we add a step to look in compile_state._dict_values more thoroughly
for the keys we need to determine "identity insert" or not, and also
add a new compiler variable dml_compile_state so that we can skip the
ORM's compile_state if present.
Fixes: #8210
Change-Id: Idbd76bb3eb075c647dc6c1cb78f7315c821e15f7
(cherry picked from commit
5806428800d2f1ac775156f90497a2fc3a644f35)
--- /dev/null
+.. change::
+ :tags: bug, mssql
+ :tickets: 8210
+
+ Fixed issues that prevented the new usage patterns for using DML with ORM
+ objects presented at :ref:`orm_dml_returning_objects` from working
+ correctly with the SQL Server pyodbc dialect.
+
)
if insert_has_identity:
- compile_state = self.compiled.compile_state
+ compile_state = self.compiled.dml_compile_state
self._enable_identity_insert = (
id_column.key in self.compiled_parameters[0]
) or (
compile_state._dict_parameters
- and (
- id_column.key in compile_state._dict_parameters
- or id_column in compile_state._dict_parameters
- )
+ and (id_column.key in compile_state._insert_col_keys)
)
else:
"""
+ dml_compile_state = None
+ """Optional :class:`.CompileState` assigned at the same point that
+ .isinsert, .isupdate, or .isdelete is assigned.
+
+ This will normally be the same object as .compile_state, with the
+ exception of cases like the :class:`.ORMFromStatementCompileState`
+ object.
+
+ .. versionadded:: 1.4.40
+
+ """
+
cache_key = None
_gen_time = None
if toplevel:
self.isinsert = True
+ if not self.dml_compile_state:
+ self.dml_compile_state = compile_state
if not self.compile_state:
self.compile_state = compile_state
toplevel = not self.stack
if toplevel:
self.isupdate = True
+ if not self.dml_compile_state:
+ self.dml_compile_state = compile_state
if not self.compile_state:
self.compile_state = compile_state
toplevel = not self.stack
if toplevel:
self.isdelete = True
+ if not self.dml_compile_state:
+ self.dml_compile_state = compile_state
if not self.compile_state:
self.compile_state = compile_state
if statement._multi_values:
self._process_multi_values(statement)
+ @util.memoized_property
+ def _insert_col_keys(self):
+ # this is also done in crud.py -> _key_getters_for_crud_column
+ return [
+ coercions.expect_as_key(roles.DMLColumnRole, col)
+ for col in self._dict_parameters
+ ]
+
@CompileState.plugin_for("default", "update")
class UpdateDMLState(DMLState):
[User(name="jack", age=52), User(name="jill", age=34)],
)
- def test_load_from_insert(self, connection):
+ @testing.combinations(
+ ("single",),
+ ("multiple", testing.requires.multivalues_inserts),
+ argnames="params",
+ )
+ def test_load_from_insert(self, connection, params):
User = self.classes.User
- stmt = (
- insert(User)
- .values({User.id: 5, User.age: 25, User.name: "spongebob"})
- .returning(User)
- )
+ if params == "multiple":
+ values = [
+ {User.id: 5, User.age: 25, User.name: "spongebob"},
+ {User.id: 6, User.age: 30, User.name: "patrick"},
+ {User.id: 7, User.age: 35, User.name: "squidward"},
+ ]
+ elif params == "single":
+ values = {User.id: 5, User.age: 25, User.name: "spongebob"}
+ else:
+ assert False
+
+ stmt = insert(User).values(values).returning(User)
stmt = select(User).from_statement(stmt)
with Session(connection) as sess:
rows = sess.execute(stmt).scalars().all()
- eq_(
- rows,
- [User(name="spongebob", age=25)],
- )
+ if params == "multiple":
+ eq_(
+ rows,
+ [
+ User(name="spongebob", age=25),
+ User(name="patrick", age=30),
+ User(name="squidward", age=35),
+ ],
+ )
+ elif params == "single":
+ eq_(
+ rows,
+ [User(name="spongebob", age=25)],
+ )
+ else:
+ assert False
from sqlalchemy.testing.schema import Table
+class ExpectExpr:
+ def __init__(self, element):
+ self.element = element
+
+ def __clause_element__(self):
+ return self.element
+
+
class InsertExecTest(fixtures.TablesTest):
__backend__ = True
)
@testing.requires.multivalues_inserts
- def test_multivalues_insert(self, connection):
+ @testing.combinations("string", "column", "expect", argnames="keytype")
+ def test_multivalues_insert(self, connection, keytype):
+
users = self.tables.users
+
+ if keytype == "string":
+ user_id, user_name = "user_id", "user_name"
+ elif keytype == "column":
+ user_id, user_name = users.c.user_id, users.c.user_name
+ elif keytype == "expect":
+ user_id, user_name = ExpectExpr(users.c.user_id), ExpectExpr(
+ users.c.user_name
+ )
+ else:
+ assert False
+
connection.execute(
users.insert().values(
[
- {"user_id": 7, "user_name": "jack"},
- {"user_id": 8, "user_name": "ed"},
+ {user_id: 7, user_name: "jack"},
+ {user_id: 8, user_name: "ed"},
]
)
)