diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index b958bf112a9309..05180a3616c5d4 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -898,6 +898,14 @@ def test_rowcount_executemany(self): self.cu.executemany("insert into test(name) values (?)", [(1,), (2,), (3,)]) self.assertEqual(self.cu.rowcount, 3) + @unittest.skipIf(sqlite.sqlite_version_info < (3, 35, 0), + "Requires SQLite 3.35.0 or newer") + def test_rowcount_update_returning(self): + # gh-93421: rowcount is updated correctly for UPDATE...RETURNING queries + self.cu.execute("update test set name='bar' where name='foo' returning 1") + self.assertEqual(self.cu.fetchone()[0], 1) + self.assertEqual(self.cu.rowcount, 1) + def test_total_changes(self): self.cu.execute("insert into test(name) values ('foo')") self.cu.execute("insert into test(name) values ('foo')") diff --git a/Misc/NEWS.d/next/Library/2022-06-05-22-22-42.gh-issue-93421.43UO_8.rst b/Misc/NEWS.d/next/Library/2022-06-05-22-22-42.gh-issue-93421.43UO_8.rst new file mode 100644 index 00000000000000..9e1d6554e0ab2b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-05-22-22-42.gh-issue-93421.43UO_8.rst @@ -0,0 +1,3 @@ +Update :data:`sqlite3.Cursor.rowcount` when a DML statement has run to +completion. This fixes the row count for SQL queries like +``UPDATE ... RETURNING``. Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index c58def5f0362f1..414584d8ee30e9 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -835,10 +835,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation stmt_reset(self->statement); } - /* reset description and rowcount */ + /* reset description */ Py_INCREF(Py_None); Py_SETREF(self->description, Py_None); - self->rowcount = 0L; if (self->statement) { (void)stmt_reset(self->statement); @@ -867,6 +866,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation stmt_reset(self->statement); stmt_mark_dirty(self->statement); + self->rowcount = self->statement->is_dml ? 0L : -1L; /* We start a transaction implicitly before a DML statement. SELECT is the only exception. See #9924. */ @@ -944,18 +944,18 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation } } - if (self->statement->is_dml) { - self->rowcount += (long)sqlite3_changes(self->connection->db); - } else { - self->rowcount= -1L; - } - if (rc == SQLITE_DONE && !multiple) { + if (self->statement->is_dml) { + self->rowcount = (long)sqlite3_changes(self->connection->db); + } stmt_reset(self->statement); Py_CLEAR(self->statement); } if (multiple) { + if (self->statement->is_dml && rc == SQLITE_DONE) { + self->rowcount += (long)sqlite3_changes(self->connection->db); + } stmt_reset(self->statement); } Py_XDECREF(parameters); @@ -1125,6 +1125,9 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self) } int rc = stmt_step(stmt); if (rc == SQLITE_DONE) { + if (self->statement->is_dml) { + self->rowcount = (long)sqlite3_changes(self->connection->db); + } (void)stmt_reset(self->statement); } else if (rc != SQLITE_ROW) {