Skip to content

Is this a bug in mssql-python #634

Closed

Description

This is easily reproducible in mssql-python and pyodbc. However freetds based pymssql executs it with no issues.
Both pyodbc and mssql-python throw this error

cs = [column[0] for column in sql_cursor.description]
^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable

Activity

  1. sravikrishna commented on Jun 15, 2026

    @sravikrishna
    Author
  2. sravikrishna commented on Jun 15, 2026

    @sravikrishna
    Author

    Pls set the server name, database name and the login id / password and run this script with all the 3 drivers.
    python bug.py pyodbc or mssql or pymssql

  3. sravikrishna commented on Jun 15, 2026

    @sravikrishna
    Author

    I am using version 1.9.0 of mssql-python

  4. sravikrishna commented on Jun 15, 2026

    @sravikrishna
    Author

    I am using cs to print the header of the result with column names.

  5. github-actions commented on Jun 15, 2026

    @github-actions

    Hi Ravi Krishna (@sravikrishna), 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!

  6. bewithgaurav commented on Jun 16, 2026

    @bewithgaurav
    Collaborator

    Thanks for reporting, Ravi Krishna (@sravikrishna)

    This is expected behavior per the DB-API 2.0 spec. Your batch has three statements - the first two (SELECT ... INTO #tmp1, SELECT ... INTO #tmp2) produce row counts, not result sets. After execute(), the cursor is positioned on that first row count, so cursor.description is None.

    You need cursor.nextset() to advance past each row-count result before accessing the final SELECT:

    sql_cursor.execute(sql_stmt)
     
    # Skip past the two SELECT INTO row counts
    sql_cursor.nextset()
    sql_cursor.nextset()
    
    # Now cursor.description has the column metadata
    cs = [column[0] for column in sql_cursor.description]
    while row := sql_cursor.fetchone():
        print(row)

    This is the same reason pyodbc behaves identically, both follow the ODBC/DB-API spec.
    pymssql (FreeTDS) auto-skips past statements that don't return rows (like SELECT INTO), which is convenient but non-standard.

    This behaviour is by-design. Hope that helps!

  7. added
    triage doneIssues that are triaged by dev team and are in investigation.
    area: api-compliancePython API behavior and typing: DB-API 2.0, exceptions, type stubs, new APIs.
    and removed
    triage neededFor new issues, not triaged yet.
    on Jun 16, 2026
  8. sravikrishna commented on Jun 16, 2026

    @sravikrishna
    Author

    Well it explains, but I am not sure I am convinced. Same sql runs fine on JDBC, SSMS, sqlcmd.

    Eventually it will become a tribal knowledge I need to keep in mind while working with pyodbc or mssql.

    Thanks for the explanation,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area: api-compliancePython API behavior and typing: DB-API 2.0, exceptions, type stubs, new APIs.questionFurther information is requestedtriage doneIssues that are triaged by dev team and are in investigation.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions