Cursor.nextset() does not update rowcount for the next result
I maintainer di solito rispondono entro 2 giorni
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 82/100
Direzione di ricerca
Inizia da Cursor_nextset e dall’implementazione di execute() per confrontare come ciascuno aggiorna rowcount; l’issue nomina questi punti di ingresso, ma non i relativi percorsi dei file. Poi leggi i test esistenti del cursore in tests/test_004_cursor.py e aggiungi test per conteggi successivi di righe interessate e un set di risultati. Il lavoro è terminato quando rowcount riflette ogni risultato dopo nextset() ed è -1 per un set di risultati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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 bd0aaf89 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.
- Lingua principale
- Python
- Stelle
- 481
- Fork
- 62
- Merge medio
- 2g 9h
- PR unite (30g)
- 11
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/mssql-python
-
triage needed
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
microsoft/mssql-python#862 · 2 commenti ·
I maintainer di solito rispondono entro 2 giorni
-
Pooled connection checkout has no timeout: connect() hangs ~16 min on a dead socketForse già presa @jahnvi480 l’ha presa 4 giorni fa. Apertaarea: connectivity-auth bug inADO triage done under development
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
microsoft/mssql-python#854 · 3 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
triage needed
Difficoltà 5/5 Più di una settimana Idoneità per principianti 38/100
microsoft/mssql-python#850 · 2 commenti · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
bulkcopy() & bulkcopy_arrow() fail with Schannel AcquireCredentialsHandleW error 0x8009030D (SEC_E_UNKNOWN_CREDENTIALS)Forse già presa @jahnvi480 l’ha presa 6 giorni fa. Apertaarea: bulk-copy bug inADO triage done
Difficoltà 4/5 3-5 giorni Idoneità per principianti 20/100
microsoft/mssql-python#848 · 5 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
macOS: encrypted connections fail after Homebrew moved the `openssl` alias to `openssl@4`Forse già presa @bewithgaurav l’ha presa 26 giorni fa. Apertaarea: packaging-platform bug triage needed
Difficoltà 4/5 3-5 giorni Idoneità per principianti 55/100
microsoft/mssql-python#826 · 2 commenti · 3 reazioni · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di microsoft/mssql-python
Issue simili
-
Link Checker ReportApertaautomated issue linkchecker report
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
QuantEcon/lecture-python.myst#1098 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
podly-pure-podcasts/podly_pure_podcasts#279 ·
I maintainer di solito rispondono entro 1 giorno
-
New template proposal: ljpApertatemplate-proposal
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
OpenShot/openshot-qt#6230 ·
I maintainer di solito rispondono entro 1 giorno