mssql-python 1.9.0: Row-friendly Bulk Copy, smarter NULL parameters, and a more portable wheel
We’re excited to announce the release of mssql-python 1.9.0, the official Microsoft SQL driver for Python. This version enhances user experience when loading data, fixes a long-standing issue with NULL parameter binding, and includes a build modification that ensures it works smoothly on clean macOS and Linux installations.
pip install --upgrade mssql-pythonIn the previous versions, Bulk Copy required you to use tuples explicitly. However, in version 1.9.0, you can simply pass Row objects directly from a SELECT query or use plain lists. The driver now automatically converts each row into a tuple before sending the data to the Rust backend.
rows = source_cursor.execute(
"SELECT id, display_name, created_at FROM users"
).fetchall()
target_cursor.bulkcopy("staging.users", rows)This common pattern of fetching data from one table and bulk inserting it into another now works effortlessly, without the need to reshape rows manually or encounter type errors at the interface.
Previously, binding None to a parameter would result in the driver defaulting to SQL_VARCHAR. While this was acceptable for character columns, it caused issues for other types, particularly VARBINARY and entirely NULL columns, where the server had no typographical signals to rely on.
With version 1.9.0, we’ve added a thread-safe, per-statement cache for SQLDescribeParam results, ensuring that NULL parameters are resolved to their actual declared types. This cache refreshes when preparing new statements, which also reduces unnecessary round-trips to the server during repeated executions of the same prepared statement.
The published wheels, especially the macOS universal2 wheel, previously linked simdutf dynamically to a path that only existed on the CI build machine. As a result, executing import mssql_python on a fresh installation could lead to missing symbol or dlopen errors.
Now, the build process no longer relies on find_package(simdutf). Instead, we use FetchContent unconditionally, with simdutf compiled as a static library and its symbols embedded directly into the extension. Users won’t have to make any additional changes; simply reinstall the wheel, and everything should import perfectly on a clean machine. A big thank you to @edgarrmondragon for their contribution.
- Improved handling of large Decimal values: Batch inserts with Decimal values exceeding the SQL Server MONEY range previously raised an SQL_C_NUMERIC type mismatch during runtime. In the current update, executemany binds DECIMAL or NUMERIC parameters as SQL_C_CHAR, which adapts the column size to fit the largest string representation, allowing large decimal batches (including NULLs and multiple-column inserts) to succeed.
- Exception pickle round-trips: Instances of the ConnectionStringParseError and its DB-API exception subclasses now support __reduce__, ensuring that driver exceptions can survive pickle and copy.deepcopy without losing any attributes. This means multiprocessing and distributed task queues can now share exceptions across process boundaries while retaining context.
- Strengthened nextset() and PRINT output collection: The nextset() function now captures diagnostic messages whenever SQL returns SQL_SUCCESS_WITH_INFO. Previously, PRINT output from secondary result sets in multi-statement batches and stored procedures was lost after the first set.
- Executemany data-at-execution with Row objects: The DAE fallback used for large columns, such as varchar(max), only recognised primitive types, which caused failures when passing Row objects. This has been amended so that Row objects are converted to tuples before type mapping.
- Type-checking improvements for fetch methods: The handling of catalog and metadata result sets no longer monkey-patches fetchone, fetchmany, and fetchall as instance attributes. Instead, a cached _column_map is generated, allowing the fetch APIs to remain proper class methods and preventing static type checkers like ty from triggering errors during cursor fetch calls.
For most users, running
pip install --upgrade mssql-pythonshould be all that’s needed. If you had previously set up workarounds for NULL parameter typing (like manually adjusting input sizes, casting in SQL, or sending sentinel values), you can now remove these. Likewise, if you were converting fetched rows into tuples for Bulk Copy or executemany, that’s no longer necessary.
A huge thank you to everyone who reported issues, provided reproductions, and reviewed pull requests in this cycle. The fixes for NULL parameters, the handling of Row objects in Bulk Copy and executemany, and the changes regarding simdutf linking were all driven by user feedback. The latter even includes a community contribution from @edgarrmondragon.
For the complete changelog and list of pull requests, visit: microsoft/mssql-python releases.
Share this content:
Discover more from Qureshi
Subscribe to get the latest posts sent to your email.