FEAT: Support for Complex Data Type- sql_variant#446
FEAT: Support for Complex Data Type- sql_variant#446gargsaumya wants to merge 9 commits intomainfrom
Conversation
- Add SQL_VARIANT constant (-150) to constants.py - Implement preprocessing approach in ddbc_bindings.cpp: * MapVariantCTypeToSQLType helper maps C types to SQL types * SQLGetData_wrap detects sql_variant and maps to base type * Handles old-style date/time C codes (9, 10, 11) * Handles SQL Server TIME type (code 16384) * Routes to existing type conversion logic (no duplication) - Move LOB detection before calculateRowSize in FetchAll_wrap - Add comprehensive test suite (25 tests): * Tests all SQL base types: INT, BIGINT, SMALLINT, TINYINT, REAL, FLOAT, DECIMAL, NUMERIC, BIT, VARCHAR, NVARCHAR, DATE, TIME, DATETIME, DATETIME2, VARBINARY, UNIQUEIDENTIFIER, NULL * Tests all fetch methods: fetchone(), fetchmany(), fetchall() * Tests implicit vs explicit type casting * All tests passing Type mappings: - Integer types → Python int - Float types → Python float - Exact numeric → Python Decimal - Character types → Python str - Date/time types → Python date/time/datetime objects - Binary → Python bytes - GUID → Python str/UUID - NULL → Python None
There was a problem hiding this comment.
Pull request overview
Adds sql_variant read support to the MSSQL Python driver by introducing the SQL Server-specific type constant and enhancing the C++ fetch path to resolve and deserialize sql_variant values as their underlying base types, with a new test suite to validate behavior across fetch methods.
Changes:
- Added
SQL_SS_VARIANT (-150)to Python constants and allowed it inSQLTypes.get_valid_types(). - Updated the C++ fetch path to detect
sql_variant, map its underlying type, and route decoding through existing type handlers; adjusted LOB detection to force streaming forsql_variant. - Added a dedicated pytest suite covering many base types and fetch patterns for
sql_variant.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
mssql_python/constants.py |
Introduces SQL_SS_VARIANT and includes it in valid SQL types for setinputsizes. |
mssql_python/cursor.py |
Adds a SQL→C type mapping entry for SQL_SS_VARIANT. |
mssql_python/pybind/ddbc_bindings.cpp |
Implements sql_variant type detection/mapping in SQLGetData_wrap and routes sql_variant through the streaming/LOB path for fetchmany/fetchall. |
tests/test_019_sql_variant.py |
New pytest coverage for fetching sql_variant across base types and fetch APIs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/pybind/ddbc_bindings.cppLines 1165-1174 1165 if (_type != SQL_HANDLE_STMT) {
1166 // Log error but don't throw - we're likely in cleanup/destructor path
1167 LOG_ERROR("SAFETY VIOLATION: Attempted to mark non-STMT handle as implicitly freed. "
1168 "Handle type=%d. This will cause handle leak. Only STMT handles are "
! 1169 "automatically freed by parent DBC handles.",
! 1170 _type);
1171 return; // Refuse to mark - let normal free() handle it
1172 }
1173 _implicitly_freed = true;
1174 }Lines 2929-2938 2929 return SQL_WVARCHAR;
2930 case SQL_C_DATE:
2931 case SQL_C_TYPE_DATE:
2932 return SQL_TYPE_DATE;
! 2933 case SQL_C_TIME:
! 2934 case SQL_C_TYPE_TIME:
2935 case SQL_SS_VARIANT_TIME:
2936 return SQL_TYPE_TIME;
2937 case SQL_C_TIMESTAMP:
2938 case SQL_C_TYPE_TIMESTAMP:Lines 2942-2954 2942 case SQL_C_GUID:
2943 return SQL_GUID;
2944 case SQL_C_NUMERIC:
2945 return SQL_NUMERIC;
! 2946 case SQL_C_TINYINT:
2947 case SQL_C_UTINYINT:
2948 case SQL_C_STINYINT:
2949 return SQL_TINYINT;
! 2950 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,Lines 2952-2960 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.
! 2956 return SQL_WVARCHAR;
2957 }
2958 }
2959
2960 // Helper function to check if a column requires SQLGetData streaming (LOB or sql_variant)Lines 3015-3026 3015 // Without this probe call, SQLColAttribute returns incorrect type codes.
3016 SQLLEN indicator;
3017 ret = SQLGetData_ptr(hStmt, i, SQL_C_BINARY, NULL, 0, &indicator);
3018 if (!SQL_SUCCEEDED(ret)) {
! 3019 LOG_ERROR("SQLGetData: Failed to probe sql_variant column %d - SQLRETURN=%d", i, ret);
! 3020 row.append(py::none());
! 3021 continue;
! 3022 }
3023 if (indicator == SQL_NULL_DATA) {
3024 row.append(py::none());
3025 continue;
3026 }Lines 3028-3039 3028 SQLLEN variantCType = 0;
3029 ret =
3030 SQLColAttribute_ptr(hStmt, i, SQL_CA_SS_VARIANT_TYPE, NULL, 0, NULL, &variantCType);
3031 if (!SQL_SUCCEEDED(ret)) {
! 3032 LOG_ERROR("SQLGetData: Failed to get sql_variant underlying type for column %d", i);
! 3033 row.append(py::none());
! 3034 continue;
! 3035 }
3036 effectiveDataType = MapVariantCTypeToSQLType(variantCType);
3037 LOG("SQLGetData: sql_variant column %d has variantCType=%ld, mapped to SQL type %d", i,
3038 (long)variantCType, effectiveDataType);
3039 }Lines 4261-4269 4261 ret = SQLFetch_ptr(hStmt);
4262 if (ret == SQL_NO_DATA)
4263 break;
4264 if (!SQL_SUCCEEDED(ret))
! 4265 return ret;
4266
4267 py::list row;
4268 SQLGetData_wrap(StatementHandle, numCols, row, charEncoding,
4269 wcharEncoding); // <-- streams LOBs correctly📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.pybind.logger_bridge.hpp: 58.8%
mssql_python.pybind.logger_bridge.cpp: 59.2%
mssql_python.row.py: 66.2%
mssql_python.pybind.ddbc_bindings.h: 69.7%
mssql_python.pybind.ddbc_bindings.cpp: 69.8%
mssql_python.pybind.connection.connection.cpp: 75.3%
mssql_python.ddbc_bindings.py: 79.6%
mssql_python.pybind.connection.connection_pool.cpp: 79.6%
mssql_python.cursor.py: 84.7%
mssql_python.__init__.py: 84.9%🔗 Quick Links
|
0c6968a to
62a83c9
Compare
62a83c9 to
a1ec891
Compare
3776aef to
c28bcb1
Compare
264fa31 to
485c22f
Compare
a9c2429 to
4d7588c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Work Item / Issue Reference
Summary
This pull request adds support for the
sql_variantSQL Server type to the Python MSSQL driver, ensuring thatsql_variantcolumns are fetched and mapped correctly to their underlying types. The changes introduce new constants, update type mappings, and enhance the fetch logic to detect and processsql_variantcolumns using their native types, improving compatibility and correctness when handling complex data.Support for sql_variant type
SQL_SS_VARIANTconstant and related attribute constants in bothmssql_python/constants.pyand the C++ binding layer to enable recognition and handling of thesql_varianttype. [1] [2]SQL_SS_VARIANTin the set of valid types for type validation logic.Type mapping and fetch logic
cursor.pyso thatSQL_SS_VARIANTis mapped toSQL_C_BINARY, allowing binary transfer of the variant data.sql_variant's underlying C type to the appropriate SQL data type, enabling the fetch logic to reuse existing code for each possible underlying type.SQLGetData_wrap,FetchMany_wrap, andFetchAll_wrap) to detectsql_variantcolumns, determine their true data types at runtime, and handle them with the correct logic. This includes always using the streaming fetch path forsql_variantcolumns to preserve native type fidelity. [1] [2] [3]Other improvements
sql_variantcolumns are processed. [1] [2] [3] [4]These changes collectively ensure that the driver can now fully support reading
sql_variantcolumns, mapping them to their actual types, and handling them efficiently.