:type query: `!str`, `!bytes`, or `sql.Composable`
:param params_seq: The parameters to pass to the query
:type params_seq: Sequence of Sequences or Mappings
+ :param returning: If `!false`, query results won't be available to fetch
+ :type returning: `!bool`
This is more efficient than performing separate queries, but in case of
several :sql:`INSERT` (and with some SQL creativity for massive
:sql:`UPDATE` too) you may consider using `copy()`.
+ If the queries return data (e.g. when executing an :sql:`INSERT ...
+ RETURNING` or a :sql:`SELECT` with a side-effect), the result will be
+ available in the cursor's state, using `fetchone()` and similar
+ methods; results after the first will be available using `nextset()`.
+ In case this makes use of an unacceptable amount of memory you can use
+ `!returning=False` to disable returning the results. This is not
+ necessary if the queries return no data (e.g. on :sql:`INSERT` without
+ returning).
+
See :ref:`query-parameters` for all the details about executing
queries.
- Results from each query, if any, can be walked through by calling
- `nextset()`.
+ .. versionchanged:: 3.1
+
+ results are now available in the cursor state, added `!returning`
+ parameter to disable it.
.. automethod:: copy
yield from self._conn._exec_command(cmd)
def _executemany_gen(
- self, query: Query, params_seq: Iterable[Params]
+ self, query: Query, params_seq: Iterable[Params], returning: bool
) -> PQGen[None]:
"""Generator implementing `Cursor.executemany()`."""
yield from self._start_query(query)
results = yield from self._maybe_prepare_gen(pgq, prepare=True)
self._check_results(results)
- self._results.extend(results)
+ if returning and results[0].status == ExecStatus.TUPLES_OK:
+ self._results.extend(results)
for res in results:
nrows += res.command_tuples or 0
raise ex.with_traceback(None)
return self
- def executemany(self, query: Query, params_seq: Iterable[Params]) -> None:
+ def executemany(
+ self,
+ query: Query,
+ params_seq: Iterable[Params],
+ *,
+ returning: bool = True,
+ ) -> None:
"""
Execute the same command with a sequence of input data.
"""
try:
with self._conn.lock:
- self._conn.wait(self._executemany_gen(query, params_seq))
+ self._conn.wait(
+ self._executemany_gen(query, params_seq, returning)
+ )
except e.Error as ex:
raise ex.with_traceback(None)
return self
async def executemany(
- self, query: Query, params_seq: Iterable[Params]
+ self,
+ query: Query,
+ params_seq: Iterable[Params],
+ *,
+ returning: bool = True,
) -> None:
try:
async with self._conn.lock:
- await self._conn.wait(self._executemany_gen(query, params_seq))
+ await self._conn.wait(
+ self._executemany_gen(query, params_seq, returning)
+ )
except e.Error as ex:
raise ex.with_traceback(None)
return self
- def executemany(self, query: Query, params_seq: Iterable[Params]) -> None:
+ def executemany(
+ self,
+ query: Query,
+ params_seq: Iterable[Params],
+ *,
+ returning: bool = True,
+ ) -> None:
"""Method not implemented for server-side cursors."""
raise e.NotSupportedError(
"executemany not supported on server-side cursors"
return self
async def executemany(
- self, query: Query, params_seq: Iterable[Params]
+ self,
+ query: Query,
+ params_seq: Iterable[Params],
+ *,
+ returning: bool = True,
) -> None:
raise e.NotSupportedError(
"executemany not supported on server-side cursors"
assert cur.nextset() is None
+def test_executemany_returning_discard(conn, execmany):
+ cur = conn.cursor()
+ cur.executemany(
+ "insert into execmany(num, data) values (%s, %s) returning num",
+ [(10, "hello"), (20, "world")],
+ returning=False,
+ )
+ assert cur.rowcount == 2
+ with pytest.raises(psycopg.ProgrammingError):
+ cur.fetchone()
+ assert cur.nextset() is None
+
+
+def test_executemany_no_result(conn, execmany):
+ cur = conn.cursor()
+ cur.executemany(
+ "insert into execmany(num, data) values (%s, %s)",
+ [(10, "hello"), (20, "world")],
+ )
+ assert cur.rowcount == 2
+ with pytest.raises(psycopg.ProgrammingError):
+ cur.fetchone()
+ assert cur.nextset() is None
+
+
def test_executemany_rowcount_no_hit(conn, execmany):
cur = conn.cursor()
cur.executemany("delete from execmany where id = %s", [(-1,), (-2,)])
assert cur.nextset() is None
+async def test_executemany_returning_discard(aconn, execmany):
+ cur = aconn.cursor()
+ await cur.executemany(
+ "insert into execmany(num, data) values (%s, %s) returning num",
+ [(10, "hello"), (20, "world")],
+ returning=False,
+ )
+ assert cur.rowcount == 2
+ with pytest.raises(psycopg.ProgrammingError):
+ await cur.fetchone()
+ assert cur.nextset() is None
+
+
+async def test_executemany_no_result(aconn, execmany):
+ cur = aconn.cursor()
+ await cur.executemany(
+ "insert into execmany(num, data) values (%s, %s)",
+ [(10, "hello"), (20, "world")],
+ )
+ assert cur.rowcount == 2
+ with pytest.raises(psycopg.ProgrammingError):
+ await cur.fetchone()
+ assert cur.nextset() is None
+
+
async def test_executemany_rowcount_no_hit(aconn, execmany):
cur = aconn.cursor()
await cur.executemany("delete from execmany where id = %s", [(-1,), (-2,)])