Repository navigation
Cursor.nextset() does not update rowcount for the next result #863
Description
Describe the bug
After Cursor.nextset() moves to the next result of a multi-statement batch, Cursor.rowcount still holds the value set by execute() for the first statement. The affected-row count of every later INSERT/UPDATE/DELETE in the batch therefore cannot be read through the DB API.
nextset() calls SQLMoreResults and refreshes description and messages, but it never calls SQLRowCount, unlike execute(). pyodbc refreshes rowcount there (Cursor_nextset calls SQLRowCount after SQLMoreResults), and nextset()'s docstring cites pyodbc parity.
This matters most for T-SQL batches with control flow. A WHILE loop that inserts rows sends one result per INSERT, and the only way to count the rows the batch changed is to read rowcount after each nextset(). Tools that report "N rows affected" per batch (migration runners, deployment CLIs) under-report, and a loop that inserted 5 rows shows 0 or 1.
No exception is raised.
To reproduce
import mssql_python
from mssql_python import ddbc_bindings
conn = mssql_python.connect(
"Server=localhost,1433;Database=tempdb;UID=sa;PWD=<password>;"
"Encrypt=yes;TrustServerCertificate=yes;"
)
cur = conn.cursor()
cur.execute("CREATE TABLE #t (id INT)")
cur.execute("""
INSERT INTO #t VALUES (1);
INSERT INTO #t VALUES (2), (3);
UPDATE #t SET id = id + 10;
""")
seen = [cur.rowcount]
native = [ddbc_bindings.DDBCSQLRowCount(cur.hstmt)]
while cur.nextset():
seen.append(cur.rowcount)
native.append(ddbc_bindings.DDBCSQLRowCount(cur.hstmt))
print("cursor.rowcount:", seen) # [1, 1, 1]
print("SQLRowCount: ", native) # [1, 2, 3]With a loop, every INSERT of the WHILE is its own result:
cur.execute("""
DECLARE @i INT = 1;
WHILE @i <= 3
BEGIN
INSERT INTO #t SELECT TOP (@i) n FROM (VALUES (1), (2), (3)) AS v(n);
SET @i = @i + 1;
END;
""")
counts = [cur.rowcount]
while cur.nextset():
counts.append(cur.rowcount)
print(counts) # [1, 1, 1] -- expected [1, 2, 3]Expected behavior
After each nextset() that returns True, rowcount reflects the result the cursor is now on, the same way it does after execute():
- the affected-row count for an INSERT/UPDATE/DELETE/MERGE result;
-1for a result set, whichfetch*()then updates as it does today.
For the first example that is [1, 2, 3]. This matches pyodbc on the same batch.
Further technical details
- Python version: 3.12
- mssql-python: 1.16.0 (also seen on 1.4.0; the code on
mainat bd0aaf8 is the same) - SQL Server version: SQL Server 2022 (16.0.4305.5, RTM-CU27-GDR)
- Operating system: Debian 12 (bookworm), in a container
Additional context
A fix is ready, and I can open a PR for it. After the describe step, nextset() would set rowcount the same way execute() does: -1 when the new result has columns, otherwise DDBCSQLRowCount(self.hstmt). It comes with two regression tests in tests/test_004_cursor.py.
I did not look at AsyncCursor.nextset(), which goes through py-core and may need its own check.
Activity
github-actions commentedon Oct 24, 2026
Hi LP Bispo (@phelipelui), thank you for opening this issue!
Our team will review it shortly. We aim to triage all new issues within 24-48 hours and get back to you.
If you have additional information to share, please feel free to update the issue.
Thank you for your patience!