top of page
  • 13 hours ago
  • 8 min read

Microsoft released mssql-python 1.14.0 on August 28, 2026 with a native C++ parameter-binding path, corrected login-timeout behavior, safer Decimal conversion errors, Arrow and bulk-copy fixes, and better Windows ARM64 extension loading.

`mssql-python` is Microsoft's official Python driver for SQL Server, Azure SQL, and SQL databases in Microsoft Fabric.

The headline performance change matters most for wide parameterized statements and batched workloads. Type detection, binding, and execution now happen in one native pipeline and cross the Python-to-native boundary once.

The most important upgrade issue is not performance, however. `connect(timeout=N)` now controls the login attempt as documented. Applications that accidentally relied on it as a query timeout must set `Connection.timeout` explicitly after upgrading.

What Changed in the Standard Execute Path

Every parameterized `execute()` call must inspect Python values, select the corresponding SQL and C types, bind the values, and invoke SQL execution.

Before 1.14.0, the driver performed type detection in a Python loop, created a `ParamInfo` object for each parameter, and moved those objects across the pybind11 boundary before the native layer could bind and execute them.

Version 1.14.0 moves the standard path into one C++ pipeline:

• Type checks use the CPython API directly.

• Parameter metadata stays in native code.

• Detection, binding, and execution share one pipeline.

• One Python-to-native call replaces per-parameter crossing overhead.

This optimization removes driver overhead. It does not remove network latency, Azure SQL execution time, locking, logging, or storage work.

Microsoft's Parameter Benchmarks

Microsoft reports that type detection fell from approximately 2.0–2.3 microseconds per parameter to about 35 nanoseconds in the benchmark behind the change.

For statements with 50 or more parameters, tested `execute()` performance improved by roughly 21% to 73% across macOS ARM64 and Linux ARM64 environments.

End-to-end insert workloads on the published macOS ARM64 test showed:

• Orders with integer, varchar, decimal, and datetime2 values: 1.57x

• Events with UUID, datetime2, varchar, and integer values: 1.55x

• Documents with roughly 10-KB `nvarchar(max)` values: 1.59x

• Wide rows with 50 mixed columns: 1.52x

Those measurements include the network round trip and SQL Server writing the rows, not only type detection.

They are benchmark results, not an Azure SQL throughput guarantee.

Narrow Statements Might Not Improve

Microsoft also tested 5,000 single-row calls with four parameters and found no meaningful change. The network round trip dominated the few microseconds removed from parameter detection.

That caveat is crucial.

The optimization is most valuable when Python-side parameter inspection represents a meaningful fraction of each call. Likely candidates include:

• Wide tables

• Generated application statements

• Batch ingestion

• ORM workloads with many bound values

• Repeated inserts with complex Python types

• Data engineering jobs that execute many parameter-heavy statements

Chatty applications issuing one tiny statement per network round trip may gain more from batching, connection pooling, query design, or regional placement than from the new binding path.

Benchmark the complete transaction.

setinputsizes Keeps the Existing Path

Calls using `setinputsizes()` continue through the existing Python detection path so explicit type overrides remain intact.

That preserves behavior, but those calls do not receive the new native optimization yet.

Inventory the codebase before testing. Two applications on the same driver version can show different results because one supplies explicit input-size metadata and the other uses standard inference.

Do not remove `setinputsizes()` merely to chase a benchmark. It might encode necessary precision, scale, length, or compatibility behavior.

Test correctness before performance.

connect(timeout) Now Means Login Timeout

The `timeout` argument passed to `connect()` is documented as the login timeout, consistent with `pyodbc`.

Before 1.14.0, mssql-python silently stored it as the per-statement query timeout. It did not bound the initial connection attempt, and a later long-running query could be canceled after that number of seconds.

The settings are now separate:

```python from mssql_python import connect

conn = connect(connection_string, timeout=5) conn.timeout = 60 ```

In this example, the connection attempt can wait up to five seconds, while each statement can run for up to 60 seconds.

An explicit `attrs_before[SQL_ATTR_LOGIN_TIMEOUT]` still takes precedence. Both timeout entry points reject negative values, non-integers, and booleans.

This correction can change production behavior after an apparently routine upgrade.

Audit Timeout Assumptions Before Upgrading

Search the codebase for:

• `connect(timeout=`

• `Connection.timeout`

• `SQL_ATTR_LOGIN_TIMEOUT`

• Connection wrappers

• ORM engine configuration

• Retry policies

• Health probes

• Bulk-copy timeout settings

For every use, write down whether the developer intended a login deadline, query deadline, or both.

After upgrading, test:

• Unreachable host

• Blocked firewall path

• Paused or unavailable database

• Slow DNS

• Slow authentication

• Long-running query

• Deadlock retry

• Connection-pool creation

• Application shutdown

A five-second login timeout and a five-second query timeout protect different failure modes.

Decimal Errors No Longer Leak Parameter Rows

Before 1.14.0, a Decimal or NUMERIC conversion failure during `executemany()` could include the entire parameter row in the exception.

That row might contain:

• Names

• Email addresses

• Account numbers

• Financial values

• Tenant identifiers

• Business data

• Secrets accidentally passed as parameters

If the exception reached an application log, APM system, support bundle, or error-tracking service, sensitive data could spread beyond the database boundary.

The new error reports only the row index, column index, and Python type. Microsoft also removed value-bearing chained exception causes that could reintroduce the data in a traceback.

This is a security and privacy improvement as much as a developer-experience fix.

Redaction Still Belongs in the Application

The driver fix protects this specific Decimal conversion path. It does not guarantee that every SQL exception, application log, ORM trace, or custom diagnostic is free of data.

Keep broader controls:

• Parameterized SQL instead of string interpolation

• Structured logging with approved fields

• Central redaction rules

• Restricted log access

• Short retention for sensitive diagnostics

• No connection strings or tokens in exception text

• Test cases that intentionally pass sensitive-looking values

Upgrade the driver, then verify the application's complete error path.

Arrow View Types Work in Bulk Copy

`bulkcopy_arrow()` now accepts variable-length Arrow View arrays, including Polars `string_view` columns exported directly through the Arrow C Data Interface.

Values and NULLs can round-trip without an explicit `DataFrame.to_arrow()` conversion.

This reduces transformation friction for Polars and other Arrow-producing workloads. It can also reduce memory copies when the application already operates in Arrow-native structures.

Validate:

• Nullable strings

• Empty strings

• Unicode

• Large values

• Schema mapping

• Column order

• Target SQL types

• Transaction and rollback behavior

Zero-copy or low-copy data movement is useful only when semantic correctness survives the transfer.

Arrow Fetch Errors Preserve the Real Failure

Defensive cleanup in the Arrow batch reader previously could raise a secondary exception that hid the original fetch failure.

Version 1.14.0 preserves the first error callers need to diagnose.

This improves observability. A cleanup exception can send engineers toward cursor state or resource disposal while the real problem was network loss, conversion, server execution, or fetch behavior.

Update tests to assert the exception type and message at the correct boundary. Monitoring rules that match old secondary errors may also need adjustment.

bulkcopy(timeout=0) Now Means Unlimited

The bulk-copy API now passes zero through as “no timeout,” matching the documented BCP contract.

Negative, non-integer, and boolean values remain invalid.

An unlimited timeout is not always operationally safe. A stuck bulk load can hold connections, locks, transactions, and pipeline capacity indefinitely.

If the application uses zero, pair it with cancellation, job-level deadlines, progress monitoring, health checks, and an operator runbook.

The driver now honors the contract. The application still owns the policy.

Windows ARM64 Loading Uses the Interpreter Architecture

On Windows ARM64, `platform.machine()` reports the host architecture. An x64 Python installation running through emulation needs the x64 native extension installed by its `win_amd64` wheel, not an ARM64 extension chosen from the host CPU.

The old loader tried the host architecture first, fell back on every import, and printed a warning to standard output.

Version 1.14.0 derives the extension architecture from the Python interpreter build. Fallback notices now use `RuntimeWarning` instead of writing directly to stdout.

This matters for command-line tools, structured-output processes, and services where unexpected standard-output text can break parsers or pollute protocol streams.

Test both native ARM64 Python and x64 Python on Windows ARM64 if the organization supports both.

Build a Representative Azure SQL Benchmark

Use a staging Azure SQL database with the same service tier, region, schema, indexes, and network path as production where possible.

Measure before and after 1.14.0:

• Rows or transactions per second

• P50, P95, and P99 call latency

• Client CPU

• Azure SQL CPU and data I/O

• Network duration

• Connection-pool behavior

• Query duration

• Errors and retries

• Memory consumption

• Log volume

Split results by statement width and execution pattern. A bulk ingestion job and an interactive API endpoint should not share one average.

Run multiple trials, warm caches consistently, and pin every dependency in the test environment.

Upgrade with a Rollback Path

For most users, Microsoft says the package can be upgraded with:

```bash pip install --upgrade mssql-python ```

Production teams should pin the exact version instead of taking an unconstrained latest dependency.

Use a staged rollout:

1. Run unit and integration tests. 2. Validate Python and platform wheels. 3. Test Entra authentication and token refresh. 4. Audit timeout semantics. 5. Compare wide and narrow workload performance. 6. Exercise Arrow and bulk-copy paths. 7. Inspect logs for sensitive values. 8. Canary one workload. 9. Monitor database and client metrics. 10. Keep the prior lockfile or image ready for rollback.

A driver sits on every database call. Treat the change as infrastructure, not a cosmetic library update.

Who Should Care?

Python application teams should care because wide parameterized statements can spend substantially less time in driver-side type detection.

Azure SQL platform teams should care because client-driver behavior affects connection pressure, ingestion throughput, timeouts, and incident diagnosis.

Data engineers should care because Arrow View support and bulk-copy fixes reduce friction in Polars and columnar pipelines.

SRE teams should care because login and query timeouts now behave as separate controls.

Security and privacy teams should care because Decimal conversion errors no longer expose full parameter rows.

Windows platform teams should care because ARM64 extension selection now follows the Python interpreter architecture.

Practical Cloud Engineer Takeaway

Upgrade first in a workload with many bound parameters, not in the smallest health-check query.

Before deployment:

1. Find every `connect(timeout=)` call and document its intended meaning. 2. Set `conn.timeout` explicitly where a query deadline is required. 3. Confirm whether `setinputsizes()` keeps the workload on the existing path. 4. Benchmark wide and batched statements end to end. 5. Test sensitive conversion errors through the logging stack. 6. Exercise Arrow, Polars, and bulk-copy paths used in production. 7. Verify the correct wheel on every supported architecture.

Publish both the performance result and the compatibility result. A faster driver that changes timeout behavior unnoticed is not a successful rollout.

Bottom Line

mssql-python 1.14.0 moves standard parameter detection, binding, and execution into one native C++ pipeline. Microsoft's benchmarks show large gains for wide parameterized statements and batched insert workloads, while narrow network-bound calls may show little difference.

The release also fixes a critical semantic mismatch: `connect(timeout=N)` now limits login time, while `Connection.timeout` controls statements. Audit existing code before upgrading.

Safer Decimal errors reduce data leakage, Arrow View support improves Polars bulk copy, original Arrow fetch errors remain visible, zero bulk-copy timeout works as documented, and Windows ARM64 loading follows the interpreter.

For Azure SQL teams, this is a worthwhile driver update with one clear rule: benchmark the real workload and test the timeout migration before production.

Sources

Microsoft SQL Server Blog, published August 28, 2026: https://techcommunity.microsoft.com/blog/sqlserver/mssql-python-1-14-0-faster-parameter-binding-safer-errors-correct-timeouts/4551366

mssql-python 1.14.0 release notes: https://github.com/microsoft/mssql-python/releases/tag/v1.14.0

mssql-python 1.14.0 on PyPI: https://pypi.org/project/mssql-python/1.14.0/

---

Stay radical, stay curious, and keep pushing the boundaries of what is possible in the cloud.

Chriz Beyond Cloud with Chriz

 
 
 

Comments


bottom of page