Skip to content

fetchone() and cursor iteration fail on ODBC catalog method results (tables, columns, etc.) while fetchall() works #505

@dlevy-msft-sql

Description

@dlevy-msft-sql

Describe the bug

fetchone() and cursor iteration fail with InterfaceError on results from ODBC catalog methods (cursor.tables(), cursor.columns(), etc.), while fetchall() works correctly on the same result set.

The error is:

InterfaceError: Driver Error: Cannot increment rownumber: no active result set.; DDBC Error: No active result set.

After calling any catalog method (e.g., cursor.tables()):

  • cursor.description is correctly populated with column metadata
  • cursor.rownumber remains -1 (never gets set to 0)
  • fetchall() works and returns all expected rows
  • fetchone() raises InterfaceError
  • for row in cursor: also fails since it calls fetchone() internally

The root cause appears to be in _increment_rownumber() — it guards against rownumber == -1 assuming no active result set, but catalog functions don't initialize rownumber to 0 the way execute() does. fetchall() bypasses this guard, which is why it works.

To reproduce

import mssql_python

conn = mssql_python.connect(
    "Server=localhost;Database=AdventureWorks2022;"
    "UID=sa;PWD=YourPassword;TrustServerCertificate=yes"
)

# --- fetchall() works ---
cursor1 = conn.cursor()
cursor1.tables(table="Product", schema="Production")
print(f"description: {cursor1.description is not None}")  # True
rows = cursor1.fetchall()
print(f"fetchall: {len(rows)} rows")  # fetchall: 1 rows
print(f"table_name: {rows[0].table_name}")  # table_name: Product
cursor1.close()

# --- fetchone() fails ---
cursor2 = conn.cursor()
cursor2.tables(table="Product", schema="Production")
print(f"description: {cursor2.description is not None}")  # True
print(f"rownumber: {cursor2.rownumber}")  # rownumber: -1  <-- should be 0
try:
    row = cursor2.fetchone()  # InterfaceError!
    print(f"fetchone: {row.table_name}")
except Exception as e:
    print(f"fetchone error: {e}")
cursor2.close()

# --- cursor iteration also fails ---
cursor3 = conn.cursor()
try:
    for row in cursor3.tables():  # InterfaceError!
        print(row.table_name)
except Exception as e:
    print(f"iteration error: {e}")
cursor3.close()

conn.close()

Output:

description: True
fetchall: 1 rows
table_name: Product
description: True
rownumber: -1
fetchone error: Driver Error: Cannot increment rownumber: no active result set.; DDBC Error: No active result set.
iteration error: Driver Error: Cannot increment rownumber: no active result set.; DDBC Error: No active result set.

Expected behavior

fetchone() and cursor iteration should work on catalog method results the same way they work for execute() results. rownumber should be initialized to 0 after a catalog method call that produces results.

Further technical details

Python version: 3.14.0 (CPython, MSC v.1944 64 bit AMD64)
SQL Server version: SQL Server 2022
Operating system: Windows 11 (10.0.26200)

Additional context

All nine ODBC catalog methods are affected:

  • cursor.tables()
  • cursor.columns()
  • cursor.procedures()
  • cursor.primaryKeys()
  • cursor.foreignKeys()
  • cursor.statistics()
  • cursor.rowIdColumns()
  • cursor.rowVerColumns()
  • cursor.getTypeInfo()

Workaround: Use cursor.tables(); rows = cursor.fetchall() instead of for row in cursor.tables():.

Driver version: mssql-python 1.4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriage neededFor new issues, not triaged yet.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions