Skip to content

FEAT: Support for Complex Data Type- sql_variant#446

Open
gargsaumya wants to merge 9 commits intomainfrom
saumya/sql_variant
Open

FEAT: Support for Complex Data Type- sql_variant#446
gargsaumya wants to merge 9 commits intomainfrom
saumya/sql_variant

Conversation

@gargsaumya
Copy link
Contributor

@gargsaumya gargsaumya commented Feb 24, 2026

Work Item / Issue Reference

AB#42724

GitHub Issue: #<ISSUE_NUMBER>


Summary

This pull request adds support for the sql_variant SQL Server type to the Python MSSQL driver, ensuring that sql_variant columns 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 process sql_variant columns using their native types, improving compatibility and correctness when handling complex data.

Support for sql_variant type

  • Added the SQL_SS_VARIANT constant and related attribute constants in both mssql_python/constants.py and the C++ binding layer to enable recognition and handling of the sql_variant type. [1] [2]
  • Included SQL_SS_VARIANT in the set of valid types for type validation logic.

Type mapping and fetch logic

  • Updated the C type mapping in cursor.py so that SQL_SS_VARIANT is mapped to SQL_C_BINARY, allowing binary transfer of the variant data.
  • Implemented a helper function in the C++ layer to map a 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.
  • Enhanced the fetch routines (SQLGetData_wrap, FetchMany_wrap, and FetchAll_wrap) to detect sql_variant columns, determine their true data types at runtime, and handle them with the correct logic. This includes always using the streaming fetch path for sql_variant columns to preserve native type fidelity. [1] [2] [3]

Other improvements

  • Improved error logging and debug output for easier troubleshooting and visibility into how sql_variant columns are processed. [1] [2] [3] [4]

These changes collectively ensure that the driver can now fully support reading sql_variant columns, mapping them to their actual types, and handling them efficiently.

- 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
Copilot AI review requested due to automatic review settings February 24, 2026 07:12
@github-actions github-actions bot added the pr-size: large Substantial code update label Feb 24, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 in SQLTypes.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 for sql_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.

@github-actions
Copy link

github-actions bot commented Feb 24, 2026

📊 Code Coverage Report

🔥 Diff Coverage

85%


🎯 Overall Coverage

76%


📈 Total Lines Covered: 5610 out of 7288
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

  • mssql_python/constants.py (100%)
  • mssql_python/pybind/ddbc_bindings.cpp (85.3%): Missing lines 1169-1170,2933-2934,2946,2950,2956,3019-3022,3032-3035,4265

Summary

  • Total: 110 lines
  • Missing: 16 lines
  • Coverage: 85%

mssql_python/pybind/ddbc_bindings.cpp

Lines 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

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants