Skip to content

Commit

Permalink
gh-93421: Update sqlite3 cursor.rowcount only after SQLITE_DONE (#93526)
Browse files Browse the repository at this point in the history
  • Loading branch information
erlend-aasland authored Jun 8, 2022
1 parent 5849af7 commit 875de61
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')")
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 11 additions & 8 deletions Modules/_sqlite/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 875de61

Please sign in to comment.