Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 2871fdb

Browse files
author
Micheal Gendy
committed
use execute_many from backends directly
1 parent 0598d92 commit 2871fdb

File tree

7 files changed

+62
-19
lines changed

7 files changed

+62
-19
lines changed

databases/backends/aiopg.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ async def execute(self, query: ClauseElement) -> typing.Any:
168168
finally:
169169
cursor.close()
170170

171-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
171+
async def execute_many(
172+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
173+
) -> None:
172174
assert self._connection is not None, "Connection is not acquired"
173175
cursor = await self._connection.cursor()
174176
try:

databases/backends/asyncmy.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ async def execute(self, query: ClauseElement) -> typing.Any:
158158
finally:
159159
await cursor.close()
160160

161-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
161+
async def execute_many(
162+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
163+
) -> None:
162164
assert self._connection is not None, "Connection is not acquired"
163165
async with self._connection.cursor() as cursor:
164166
try:

databases/backends/mysql.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,14 @@ async def execute(self, query: ClauseElement) -> typing.Any:
158158
finally:
159159
await cursor.close()
160160

161-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
161+
async def execute_many(
162+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
163+
) -> None:
162164
assert self._connection is not None, "Connection is not acquired"
163165
cursor = await self._connection.cursor()
166+
query_str, values = self._compile_many(queries, values)
164167
try:
165-
for single_query in queries:
166-
single_query, args, context = self._compile(single_query)
167-
await cursor.execute(single_query, args)
168+
await cursor.executemany(query_str, values)
168169
finally:
169170
await cursor.close()
170171

@@ -220,6 +221,18 @@ def _compile(
220221
logger.debug("Query: %s Args: %s", query_message, repr(args), extra=LOG_EXTRA)
221222
return compiled.string, args, CompilationContext(execution_context)
222223

224+
def _compile_many(
225+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
226+
) -> typing.Tuple[str, list]:
227+
compiled = queries[0].compile(
228+
dialect=self._dialect, compile_kwargs={"render_postcompile": True}
229+
)
230+
for args in values:
231+
for key, val in args.items():
232+
if key in compiled._bind_processors:
233+
args[key] = compiled._bind_processors[key](val)
234+
return compiled.string, values
235+
223236
@property
224237
def raw_connection(self) -> aiomysql.connection.Connection:
225238
assert self._connection is not None, "Connection is not acquired"

databases/backends/postgres.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22
import typing
3-
from collections.abc import Sequence
43

54
import asyncpg
65
from sqlalchemy.dialects.postgresql import pypostgresql
@@ -217,14 +216,12 @@ async def execute(self, query: ClauseElement) -> typing.Any:
217216
query_str, args, result_columns = self._compile(query)
218217
return await self._connection.fetchval(query_str, *args)
219218

220-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
219+
async def execute_many(
220+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
221+
) -> None:
221222
assert self._connection is not None, "Connection is not acquired"
222-
# asyncpg uses prepared statements under the hood, so we just
223-
# loop through multiple executes here, which should all end up
224-
# using the same prepared statement.
225-
for single_query in queries:
226-
single_query, args, result_columns = self._compile(single_query)
227-
await self._connection.execute(single_query, *args)
223+
query_str, values = self._compile_many(queries, values)
224+
await self._connection.executemany(query_str, values)
228225

229226
async def iterate(
230227
self, query: ClauseElement
@@ -269,6 +266,18 @@ def _compile(self, query: ClauseElement) -> typing.Tuple[str, list, tuple]:
269266
)
270267
return compiled_query, args, result_map
271268

269+
def _compile_many(
270+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
271+
) -> typing.Tuple[str, list]:
272+
compiled = queries[0].compile(
273+
dialect=self._dialect, compile_kwargs={"render_postcompile": True}
274+
)
275+
for args in values:
276+
for key, val in args.items():
277+
if key in compiled._bind_processors:
278+
args[key] = compiled._bind_processors[key](val)
279+
return compiled.string, values
280+
272281
@staticmethod
273282
def _create_column_maps(
274283
result_columns: tuple,

databases/backends/sqlite.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,13 @@ async def execute(self, query: ClauseElement) -> typing.Any:
135135
return cursor.rowcount
136136
return cursor.lastrowid
137137

138-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
138+
async def execute_many(
139+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
140+
) -> None:
139141
assert self._connection is not None, "Connection is not acquired"
140-
for single_query in queries:
141-
await self.execute(single_query)
142+
query_str, values = self._compile_many(queries, values)
143+
async with self._connection.cursor() as cursor:
144+
await cursor.executemany(query_str, values)
142145

143146
async def iterate(
144147
self, query: ClauseElement
@@ -194,6 +197,18 @@ def _compile(
194197
)
195198
return compiled.string, args, CompilationContext(execution_context)
196199

200+
def _compile_many(
201+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
202+
) -> typing.Tuple[str, list]:
203+
compiled = queries[0].compile(
204+
dialect=self._dialect, compile_kwargs={"render_postcompile": True}
205+
)
206+
for args in values:
207+
for key, val in args.items():
208+
if key in compiled._bind_processors:
209+
args[key] = compiled._bind_processors[key](val)
210+
return compiled.string, values
211+
197212
@property
198213
def raw_connection(self) -> aiosqlite.core.Connection:
199214
assert self._connection is not None, "Connection is not acquired"

databases/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ async def execute_many(
304304
) -> None:
305305
queries = [self._build_query(query, values_set) for values_set in values]
306306
async with self._query_lock:
307-
await self._connection.execute_many(queries)
307+
await self._connection.execute_many(queries, values)
308308

309309
async def iterate(
310310
self, query: typing.Union[ClauseElement, str], values: dict = None

databases/interfaces.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ async def fetch_val(
3737
async def execute(self, query: ClauseElement) -> typing.Any:
3838
raise NotImplementedError() # pragma: no cover
3939

40-
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
40+
async def execute_many(
41+
self, queries: typing.List[ClauseElement], values: typing.List[dict]
42+
) -> None:
4143
raise NotImplementedError() # pragma: no cover
4244

4345
async def iterate(

0 commit comments

Comments
 (0)