From 03e7b4f7e50075aed8fd3808e44cca2d52b2aa5a Mon Sep 17 00:00:00 2001 From: gargsaumya Date: Tue, 3 Mar 2026 16:06:16 +0530 Subject: [PATCH] Fix NULL parameter type mapping for VARBINARY columns (#458) --- mssql_python/cursor.py | 12 +++++++++++- mssql_python/pybind/ddbc_bindings.cpp | 15 ++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mssql_python/cursor.py b/mssql_python/cursor.py index 316d8ed9..d0e40c75 100644 --- a/mssql_python/cursor.py +++ b/mssql_python/cursor.py @@ -392,7 +392,7 @@ def _map_sql_type( # pylint: disable=too-many-arguments,too-many-positional-arg if param is None: logger.debug("_map_sql_type: NULL parameter - index=%d", i) return ( - ddbc_sql_const.SQL_VARCHAR.value, + ddbc_sql_const.SQL_UNKNOWN_TYPE.value, ddbc_sql_const.SQL_C_DEFAULT.value, 1, 0, @@ -2185,6 +2185,16 @@ def executemany( # pylint: disable=too-many-locals,too-many-branches,too-many-s min_val=min_val, max_val=max_val, ) + + # For executemany with all-NULL columns, SQL_UNKNOWN_TYPE doesn't work + # with array binding. Fall back to SQL_VARCHAR as a safe default. + if ( + sample_value is None + and paraminfo.paramSQLType == ddbc_sql_const.SQL_UNKNOWN_TYPE.value + ): + paraminfo.paramSQLType = ddbc_sql_const.SQL_VARCHAR.value + paraminfo.columnSize = 1 + # Special handling for binary data in auto-detected types if paraminfo.paramSQLType in ( ddbc_sql_const.SQL_BINARY.value, diff --git a/mssql_python/pybind/ddbc_bindings.cpp b/mssql_python/pybind/ddbc_bindings.cpp index 0933d4fa..8af6f484 100644 --- a/mssql_python/pybind/ddbc_bindings.cpp +++ b/mssql_python/pybind/ddbc_bindings.cpp @@ -471,14 +471,19 @@ SQLRETURN BindParameters(SQLHANDLE hStmt, const py::list& params, hStmt, static_cast(paramIndex + 1), &describedType, &describedSize, &describedDigits, &nullable); if (!SQL_SUCCEEDED(rc)) { + // SQLDescribeParam can fail for generic SELECT statements where + // no table column is referenced. Fall back to SQL_VARCHAR as a safe default. LOG("BindParameters: SQLDescribeParam failed for " - "param[%d] (NULL parameter) - SQLRETURN=%d", + "param[%d] (NULL parameter) - SQLRETURN=%d, falling back to SQL_VARCHAR", paramIndex, rc); - return rc; + sqlType = SQL_VARCHAR; + columnSize = 1; + decimalDigits = 0; + } else { + sqlType = describedType; + columnSize = describedSize; + decimalDigits = describedDigits; } - sqlType = describedType; - columnSize = describedSize; - decimalDigits = describedDigits; } dataPtr = nullptr; strLenOrIndPtr = AllocateParamBuffer(paramBuffers);