Skip to content

setinputsizes() with SQL_DECIMAL/SQL_NUMERIC crashes with C type mismatch #503

@dlevy-msft-sql

Description

@dlevy-msft-sql

Describe the bug

Calling cursor.setinputsizes() with SQL_DECIMAL or SQL_NUMERIC type tuples causes a RuntimeError when followed by executemany(). The C binding rejects Python Decimal objects because _get_c_type_for_sql_type maps SQL_DECIMAL/SQL_NUMERIC to SQL_C_NUMERIC, but the binding doesn't convert Decimal to the SQL_NUMERIC_STRUCT that ODBC expects.

Without setinputsizes(), the driver infers the correct type and inserts succeed.

Exception message:
RuntimeError: Parameter's object type does not match parameter's C type. paramIndex - 2, C type - SQL_C_NUMERIC

To reproduce

import mssql_python
from decimal import Decimal

conn = mssql_python.connect(
    "Server=myserver;Database=mydb;UID=user;PWD=pass;"
    "TrustServerCertificate=yes;Encrypt=yes"
)
cursor = conn.cursor()

cursor.execute("""
    IF OBJECT_ID('dbo.SetInputSizesTest') IS NOT NULL
        DROP TABLE dbo.SetInputSizesTest;
    CREATE TABLE dbo.SetInputSizesTest (
        Name NVARCHAR(100),
        CategoryID INT,
        Price DECIMAL(18,2)
    )
""")
conn.commit()

# This works fine - no setinputsizes
cursor.executemany(
    "INSERT INTO dbo.SetInputSizesTest (Name, CategoryID, Price) VALUES (?, ?, ?)",
    [("Widget", 1, Decimal("19.99")), ("Gadget", 2, Decimal("29.99"))]
)
conn.commit()
print("Without setinputsizes: OK")

cursor.execute("DELETE FROM dbo.SetInputSizesTest")
conn.commit()

# This crashes
cursor.setinputsizes([
    (mssql_python.SQL_WVARCHAR, 100, 0),   # nvarchar(100) - works
    (mssql_python.SQL_INTEGER, 0, 0),       # int - works
    (mssql_python.SQL_DECIMAL, 18, 2)       # decimal(18,2) - crashes
])

cursor.executemany(
    "INSERT INTO dbo.SetInputSizesTest (Name, CategoryID, Price) VALUES (?, ?, ?)",
    [("Widget", 1, Decimal("19.99")), ("Gadget", 2, Decimal("29.99"))]
)
# RuntimeError: Parameter's object type does not match parameter's C type.
# paramIndex - 2, C type - SQL_C_NUMERIC

Expected behavior

setinputsizes() with SQL_DECIMAL should accept Python Decimal values and insert them into a DECIMAL(18,2) column, the same way the driver handles them when type inference is used (without setinputsizes).

Further technical details

Python version: 3.14.0
mssql-python version: 1.4.0 (also confirmed on main branch source)
SQL Server version: SQL Server 2022
Operating system: Windows 11 (ARM64, using x64 fallback module)

Additional context

The root cause is in cursor.py -> _get_c_type_for_sql_type():

SQL_DECIMAL: SQL_C_NUMERIC,
SQL_NUMERIC: SQL_C_NUMERIC,

The C binding expects an SQL_NUMERIC_STRUCT for SQL_C_NUMERIC but receives a Python Decimal object. The same issue occurs with SQL_NUMERIC.

Workaround: Omit SQL_DECIMAL/SQL_NUMERIC from setinputsizes() and let the driver infer the type automatically, which works correctly.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinginADO

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions