Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 95628d9

Browse files
zzzeekGerrit Code Review
authored andcommitted
Merge "try to omit unnecessary cols for ORM bulk insert + returning" into main
2 parents 98b77c3 + 63f5149 commit 95628d9

File tree

4 files changed

+93
-30
lines changed

4 files changed

+93
-30
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. change::
2+
:tags: bug, orm
3+
:tickets: 9685
4+
5+
Fixed bug in ORM bulk insert feature where additional unnecessary columns
6+
would be rendered in the INSERT statement if RETURNING of individual columns
7+
were requested.

lib/sqlalchemy/orm/bulk_persistence.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,9 @@ def _setup_orm_returning(
464464
compiler,
465465
orm_level_statement,
466466
dml_level_statement,
467+
dml_mapper,
468+
*,
467469
use_supplemental_cols=True,
468-
dml_mapper=None,
469470
):
470471
"""establish ORM column handlers for an INSERT, UPDATE, or DELETE
471472
which uses explicit returning().
@@ -504,7 +505,17 @@ def _setup_orm_returning(
504505

505506
if use_supplemental_cols:
506507
dml_level_statement = dml_level_statement.return_defaults(
507-
supplemental_cols=cols_to_return
508+
# this is a little weird looking, but by passing
509+
# primary key as the main list of cols, this tells
510+
# return_defaults to omit server-default cols. Since
511+
# we have cols_to_return, just return what we asked for
512+
# (plus primary key, which ORM persistence needs since
513+
# we likely set bookkeeping=True here, which is another
514+
# whole thing...). We dont want to clutter the
515+
# statement up with lots of other cols the user didn't
516+
# ask for. see #9685
517+
*dml_mapper.primary_key,
518+
supplemental_cols=cols_to_return,
508519
)
509520
else:
510521
dml_level_statement = dml_level_statement.returning(
@@ -1280,6 +1291,7 @@ def _setup_for_orm_insert(self, compiler, mapper):
12801291
compiler,
12811292
orm_level_statement,
12821293
statement,
1294+
dml_mapper=mapper,
12831295
use_supplemental_cols=False,
12841296
)
12851297
self.statement = statement
@@ -1314,8 +1326,8 @@ def _setup_for_bulk_insert(self, compiler):
13141326
compiler,
13151327
orm_level_statement,
13161328
statement,
1317-
use_supplemental_cols=True,
13181329
dml_mapper=emit_insert_mapper,
1330+
use_supplemental_cols=True,
13191331
)
13201332

13211333
self.statement = statement
@@ -1425,6 +1437,7 @@ def _setup_for_orm_update(self, statement, compiler, **kw):
14251437
compiler,
14261438
orm_level_statement,
14271439
new_stmt,
1440+
dml_mapper=mapper,
14281441
use_supplemental_cols=use_supplemental_cols,
14291442
)
14301443

@@ -1795,6 +1808,7 @@ def create_for_statement(cls, statement, compiler, **kw):
17951808
compiler,
17961809
orm_level_statement,
17971810
new_stmt,
1811+
dml_mapper=mapper,
17981812
use_supplemental_cols=use_supplemental_cols,
17991813
)
18001814

lib/sqlalchemy/orm/persistence.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,12 +1076,16 @@ def _emit_insert_statements(
10761076
else:
10771077
do_executemany = False
10781078

1079-
if not has_all_defaults and base_mapper._prefer_eager_defaults(
1080-
connection.dialect, table
1081-
):
1082-
statement = statement.return_defaults(
1083-
*mapper._server_default_cols[table]
1084-
)
1079+
if use_orm_insert_stmt is None:
1080+
if (
1081+
not has_all_defaults
1082+
and base_mapper._prefer_eager_defaults(
1083+
connection.dialect, table
1084+
)
1085+
):
1086+
statement = statement.return_defaults(
1087+
*mapper._server_default_cols[table]
1088+
)
10851089

10861090
if mapper.version_id_col is not None:
10871091
statement = statement.return_defaults(mapper.version_id_col)

test/orm/dml/test_bulk_statements.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from sqlalchemy.testing.fixtures import fixture_session
3434

3535

36-
class InsertStmtTest(fixtures.TestBase):
36+
class InsertStmtTest(testing.AssertsExecutionResults, fixtures.TestBase):
3737
def test_no_returning_error(self, decl_base):
3838
class A(fixtures.ComparableEntity, decl_base):
3939
__tablename__ = "a"
@@ -89,6 +89,48 @@ class A(decl_base):
8989
[("d3", 5), ("d4", 6)],
9090
)
9191

92+
@testing.requires.insert_returning
93+
def test_insert_returning_cols_dont_give_me_defaults(self, decl_base):
94+
"""test #9685"""
95+
96+
class User(decl_base):
97+
__tablename__ = "users"
98+
99+
id: Mapped[int] = mapped_column(Identity(), primary_key=True)
100+
101+
name: Mapped[str] = mapped_column()
102+
other_thing: Mapped[Optional[str]]
103+
server_thing: Mapped[str] = mapped_column(server_default="thing")
104+
105+
decl_base.metadata.create_all(testing.db)
106+
insert_stmt = insert(User).returning(User.id)
107+
108+
s = fixture_session()
109+
110+
with self.sql_execution_asserter() as asserter:
111+
result = s.execute(
112+
insert_stmt,
113+
[
114+
{"name": "some name 1"},
115+
{"name": "some name 2"},
116+
{"name": "some name 3"},
117+
],
118+
)
119+
120+
eq_(result.all(), [(1,), (2,), (3,)])
121+
122+
asserter.assert_(
123+
CompiledSQL(
124+
"INSERT INTO users (name) VALUES (:name) "
125+
"RETURNING users.id",
126+
[
127+
{"name": "some name 1"},
128+
{"name": "some name 2"},
129+
{"name": "some name 3"},
130+
],
131+
),
132+
)
133+
92134
@testing.requires.insert_returning
93135
def test_insert_from_select_col_property(self, decl_base):
94136
"""test #9273"""
@@ -191,31 +233,26 @@ def test_heterogeneous_keys(self, use_returning):
191233
with self.sql_execution_asserter() as asserter:
192234
result = s.execute(stmt, values)
193235

194-
if inspect(B).single:
195-
single_inh = ", a.bd, a.zcol, a.q"
196-
else:
197-
single_inh = ""
198-
199236
if use_returning:
200237
asserter.assert_(
201238
CompiledSQL(
202239
"INSERT INTO a (type, data, xcol) VALUES "
203240
"(:type, :data, :xcol) "
204-
f"RETURNING a.id, a.type, a.data, a.xcol, a.y{single_inh}",
241+
"RETURNING a.id, a.type, a.data, a.xcol, a.y",
205242
[
206243
{"type": "a", "data": "d3", "xcol": 5},
207244
{"type": "a", "data": "d4", "xcol": 6},
208245
],
209246
),
210247
CompiledSQL(
211248
"INSERT INTO a (type, data) VALUES (:type, :data) "
212-
f"RETURNING a.id, a.type, a.data, a.xcol, a.y{single_inh}",
249+
"RETURNING a.id, a.type, a.data, a.xcol, a.y",
213250
[{"type": "a", "data": "d5"}],
214251
),
215252
CompiledSQL(
216253
"INSERT INTO a (type, data, xcol, y) "
217254
"VALUES (:type, :data, :xcol, :y) "
218-
f"RETURNING a.id, a.type, a.data, a.xcol, a.y{single_inh}",
255+
"RETURNING a.id, a.type, a.data, a.xcol, a.y",
219256
[
220257
{"type": "a", "data": "d6", "xcol": 8, "y": 9},
221258
{"type": "a", "data": "d7", "xcol": 12, "y": 12},
@@ -224,7 +261,7 @@ def test_heterogeneous_keys(self, use_returning):
224261
CompiledSQL(
225262
"INSERT INTO a (type, data, xcol) "
226263
"VALUES (:type, :data, :xcol) "
227-
f"RETURNING a.id, a.type, a.data, a.xcol, a.y{single_inh}",
264+
"RETURNING a.id, a.type, a.data, a.xcol, a.y",
228265
[{"type": "a", "data": "d8", "xcol": 7}],
229266
),
230267
)
@@ -258,17 +295,18 @@ def test_heterogeneous_keys(self, use_returning):
258295
)
259296

260297
if use_returning:
261-
eq_(
262-
result.scalars().all(),
263-
[
264-
A(data="d3", id=mock.ANY, type="a", x=5, y=None),
265-
A(data="d4", id=mock.ANY, type="a", x=6, y=None),
266-
A(data="d5", id=mock.ANY, type="a", x=None, y=None),
267-
A(data="d6", id=mock.ANY, type="a", x=8, y=9),
268-
A(data="d7", id=mock.ANY, type="a", x=12, y=12),
269-
A(data="d8", id=mock.ANY, type="a", x=7, y=None),
270-
],
271-
)
298+
with self.assert_statement_count(testing.db, 0):
299+
eq_(
300+
result.scalars().all(),
301+
[
302+
A(data="d3", id=mock.ANY, type="a", x=5, y=None),
303+
A(data="d4", id=mock.ANY, type="a", x=6, y=None),
304+
A(data="d5", id=mock.ANY, type="a", x=None, y=None),
305+
A(data="d6", id=mock.ANY, type="a", x=8, y=9),
306+
A(data="d7", id=mock.ANY, type="a", x=12, y=12),
307+
A(data="d8", id=mock.ANY, type="a", x=7, y=None),
308+
],
309+
)
272310

273311
@testing.combinations(
274312
"strings",

0 commit comments

Comments
 (0)