@@ -2948,6 +2948,11 @@ SQLSMALLINT MapVariantCTypeToSQLType(SQLLEN variantCType) {
29482948 case SQL_C_STINYINT:
29492949 return SQL_TINYINT;
29502950 default :
2951+ // Unknown C type code - fallback to WVARCHAR for string conversion
2952+ // Note: SQL Server enforces sql_variant restrictions at INSERT time, preventing
2953+ // invalid types (text, ntext, image, timestamp, xml, MAX types, nested variants,
2954+ // spatial types, hierarchyid, UDTs) from being stored. By the time we fetch data,
2955+ // only valid base types exist. This default handles unmapped/future type codes.
29512956 return SQL_WVARCHAR;
29522957 }
29532958}
@@ -2990,15 +2995,27 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
29902995 continue ;
29912996 }
29922997
2993- // Preprocess sql_variant: detect underlying type and handle NULL
2998+ // Preprocess sql_variant: detect underlying type to route to correct conversion logic
29942999 SQLSMALLINT effectiveDataType = dataType;
29953000 if (dataType == SQL_SS_VARIANT) {
3001+ // For sql_variant, we MUST call SQLGetData with SQL_C_BINARY (NULL buffer, len=0)
3002+ // first. This serves two purposes:
3003+ // 1. Detects NULL values via the indicator parameter
3004+ // 2. Initializes the variant metadata in the ODBC driver, which is required for
3005+ // SQLColAttribute(SQL_CA_SS_VARIANT_TYPE) to return the correct underlying C type.
3006+ // Without this probe call, SQLColAttribute returns incorrect type codes.
29963007 SQLLEN indicator;
29973008 ret = SQLGetData_ptr (hStmt, i, SQL_C_BINARY, NULL , 0 , &indicator);
3009+ if (!SQL_SUCCEEDED (ret)) {
3010+ LOG (" SQLGetData: Failed to probe sql_variant column %d - SQLRETURN=%d" , i, ret);
3011+ row.append (py::none ());
3012+ continue ;
3013+ }
29983014 if (indicator == SQL_NULL_DATA) {
29993015 row.append (py::none ());
30003016 continue ;
30013017 }
3018+ // Now retrieve the underlying C type
30023019 SQLLEN variantCType = 0 ;
30033020 ret =
30043021 SQLColAttribute_ptr (hStmt, i, SQL_CA_SS_VARIANT_TYPE, NULL , 0 , NULL , &variantCType);
@@ -3008,6 +3025,8 @@ SQLRETURN SQLGetData_wrap(SqlHandlePtr StatementHandle, SQLUSMALLINT colCount, p
30083025 continue ;
30093026 }
30103027 effectiveDataType = MapVariantCTypeToSQLType (variantCType);
3028+ LOG (" SQLGetData: sql_variant column %d has variantCType=%ld, mapped to SQL type %d" , i,
3029+ (long )variantCType, effectiveDataType);
30113030 }
30123031
30133032 switch (effectiveDataType) {
0 commit comments